mirror of
https://github.com/NohamR/knowledge-kit.git
synced 2026-05-25 04:17:17 +00:00
docs: image url
This commit is contained in:
@@ -75,7 +75,7 @@ Person 类存在3个 BOOL 属性:
|
||||
|
||||
上 Demo
|
||||
|
||||
<img src="./../assets/BOOLPropertyImplementedByBitOperator.png" style="zoom:25%">
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/BOOLPropertyImplementedByBitOperator.png" style="zoom:25%">
|
||||
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ Person 类存在3个 BOOL 属性:
|
||||
|
||||
新方案采用:**结构体的位域能力**,限定单个成员变量所占用的内存。代码如下:
|
||||
|
||||
<img src="./../assets/BOOLProptertImpledByStructureBitDomain.png" style="zoom:25%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/BOOLProptertImpledByStructureBitDomain.png" style="zoom:25%" />
|
||||
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ Person 类存在3个 BOOL 属性:
|
||||
|
||||
虽然上述方式都可以实现存储 Person 类3个属性的目的,但是还有第三种方案,参考 iOS 系统设计,采用 Union 实现。代码如下
|
||||
|
||||
<img src="./../assets/BOOLPropertyImplementedByUnion.png" style="zoom:25%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/BOOLPropertyImplementedByUnion.png" style="zoom:25%" />
|
||||
|
||||
分析:
|
||||
|
||||
@@ -186,7 +186,7 @@ union {
|
||||
|
||||
与一个不是3个数之一的数按位与,得到的结果为`0b0000 0000`。利用这个特性我们可以判断传递来的参数是不是包含了某个值
|
||||
|
||||
<img src="./../assets/BitOperateorOnEnumDemo.png" style="zoom:25%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/BitOperateorOnEnumDemo.png" style="zoom:25%" />
|
||||
|
||||
有了上面的铺垫,就可以更好的查看 runtime 中 isa 的定义了。
|
||||
|
||||
@@ -325,7 +325,7 @@ isa 在 arm64 之后必须通过 `ISA_MASK` 去查询 class(类对象、元类
|
||||
|
||||
`0x0000000ffffffff8ULL` 用程序员模式打开计算器
|
||||
|
||||
<img src="./../assets/objc-isa-mask.png" style="zoom:30%">
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/objc-isa-mask.png" style="zoom:30%">
|
||||
|
||||
|
||||
|
||||
@@ -335,7 +335,7 @@ extra_rc、has_sidetable_rc、deallocating、weakly_referenced、magic、shiftcl
|
||||
|
||||
知道结构体可以指定存储大小这个功能后,可以看到 `isa_t` 联合体与 `ISA_MASK` 按位与之后的地址,其实就是类真实的地址信息(可能是类对象、也有可能是元类对象)
|
||||
|
||||
<img src="./../assets/objc-isa.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/objc-isa.png" style="zoom:30%" />
|
||||
|
||||
|
||||
|
||||
@@ -483,7 +483,7 @@ struct class_ro_t {
|
||||
|
||||
具体关系整理如下图
|
||||
|
||||
<img src="./../assets/runtime-class.png" style="zoom:50%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/runtime-class.png" style="zoom:50%" />
|
||||
|
||||
|
||||
|
||||
@@ -495,7 +495,7 @@ struct class_ro_t {
|
||||
|
||||
|
||||
|
||||
<img src="./../assets/runtime-class-rw-t.png" style="zoom:40%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/runtime-class-rw-t.png" style="zoom:40%" />
|
||||
|
||||
比如访问 method 的过程
|
||||
|
||||
@@ -513,7 +513,7 @@ struct class_ro_t {
|
||||
|
||||
- `class_ro_t` 里面的 baseMethodList、baseProtocols、ivars、baseProperties 是一维数组,是只读的,包含了类的(原始信息)初始内容
|
||||
|
||||
<img src="./../assets/runtime-class-ro-t.png" style="zoom:50%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/runtime-class-ro-t.png" style="zoom:50%" />
|
||||
|
||||
- 当系统运行 Runtime 会把类自身的信息(class_ro_t) 中的信息和 Category 中的信息合并起来,放到 class_rw_t 中的信息去
|
||||
|
||||
@@ -1431,7 +1431,7 @@ v
|
||||
|
||||
可以对照下面的表格进行查看:
|
||||
|
||||

|
||||

|
||||
|
||||
```objectivec
|
||||
- (int)calcuate:(int)baseHeight heigith:(float)height;
|
||||
@@ -2013,7 +2013,7 @@ bucket_t goodStudentSayBucket = buckets[(uintptr_t)@selector(personSay) & cache.
|
||||
NSLog(@"%s %p", goodStudentSayBucket._key, goodStudentSayBucket._imp);
|
||||
```
|
||||
|
||||
<img src="./../assets/runtime-method-find.png" style="zoom:25%">
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/runtime-method-find.png" style="zoom:25%">
|
||||
|
||||
|
||||
|
||||
@@ -2087,7 +2087,7 @@ do {
|
||||
|
||||
|
||||
|
||||
<img src="./../assets/MethodCallIsObjcMsgSend.png" style="zoom:20%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/MethodCallIsObjcMsgSend.png" style="zoom:20%" />
|
||||
|
||||
```c++
|
||||
Person *p = [[Person alloc] init];
|
||||
@@ -2658,7 +2658,7 @@ if (imp) goto done;
|
||||
|
||||
上面的流程是整个 `objc_msgSend` 的消息发送阶段的整个流程。可以用下图表示
|
||||
|
||||
<img src="./../assets/runtime-objc_msgSend-messageSend.png" style="zoom:60%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/runtime-objc_msgSend-messageSend.png" style="zoom:60%" />
|
||||
|
||||
|
||||
|
||||
@@ -2807,7 +2807,7 @@ SEL_resolveClassMethod, sel);`
|
||||
|
||||
完整流程如下
|
||||
|
||||
<img src="./../assets/runtime-objc_msgSend-ResolveMethod.png" style="zoom:100%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/runtime-objc_msgSend-ResolveMethod.png" style="zoom:100%" />
|
||||
|
||||
|
||||
|
||||
@@ -2852,7 +2852,7 @@ Person *person = [[Person alloc] init];
|
||||
|
||||
知道 `objc_msgSend` 的流程,我们尝试给它修正下
|
||||
|
||||
<img src="./../assets/objc_msgSend_fix1.png" style="zoom:25%">
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/objc_msgSend_fix1.png" style="zoom:25%">
|
||||
|
||||
方法1,增加一个兜底方法,然后利用 `class_addMethod` 动态增加方法实现
|
||||
|
||||
@@ -2886,7 +2886,7 @@ class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncod
|
||||
|
||||
方法3,也可以添加 c 语言方法
|
||||
|
||||
<img src="./../assets/objc_msgSend_fix2.png" style="zoom:25%">
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/objc_msgSend_fix2.png" style="zoom:25%">
|
||||
|
||||
c 函数即函数地址,只不过传递给 class_addMethod 的时候需要转换为函数参数加 `(IMP)cfuntionResolver`,手动添加方法签名。
|
||||
|
||||
@@ -2999,7 +2999,7 @@ void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
|
||||
|
||||
为什么是 `__forwarding__` 方法。我们可以根据 Xcode 崩溃窥探一二
|
||||
|
||||

|
||||

|
||||
|
||||
```c
|
||||
int __forwarding__(void *frameStackPointer, int isStret) {
|
||||
@@ -3043,7 +3043,7 @@ int __forwarding__(void *frameStackPointer, int isStret) {
|
||||
|
||||
完整流程如下
|
||||
|
||||
<img src="./../assets/runtime-forwarding.png" style="zoom:40%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/runtime-forwarding.png" style="zoom:40%" />
|
||||
|
||||
|
||||
|
||||
@@ -3077,13 +3077,13 @@ int __forwarding__(void *frameStackPointer, int isStret) {
|
||||
|
||||
Person 类不存在对象方法 makeliving ,PersonHelper 类存在。
|
||||
|
||||
<img src="./../assets/RuntimeCallUnKnownMethod.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RuntimeCallUnKnownMethod.png" style="zoom:30%" />
|
||||
|
||||
调用对象不存在的方法,则会抛出错误。同时在 Runtime 动态消息解析阶段,`resolveInstanceMethod` 没有处理对象方法,所以会报错
|
||||
|
||||
方法1:因为动态消息解析没有处理,则会开始走消息转发阶段。消息转发首先会调用 `- (id)forwardingTargetForSelector:(SEL)aSelector` 方法。(如果是对象方法则调用 `- (id)forwardingTargetForSelector:(SEL)aSelector`,如果是类方法则调用 `+ (id)forwardingTargetForSelector:(SEL)aSelector`)
|
||||
|
||||
<img src="./../assets/RuntimeMethodForwardingDemo1.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RuntimeMethodForwardingDemo1.png" style="zoom:30%" />
|
||||
|
||||
方法2:如果消息转发里,`forwardingTargetForSelector` 返回了 nil,则开始调用方法签名 `methodSignatureForSelector` 方法和 `forwardInvocation` 方法
|
||||
|
||||
@@ -3116,7 +3116,7 @@ Person 类不存在对象方法 makeliving ,PersonHelper 类存在。
|
||||
1. `methodSignatureForSelector` 如果返回 nil,则 `forwardInvocation` 不会执行
|
||||
2. 方法签名 `methodSignatureForSelector` 必须正确,否则会获取参数 crash,报错 `Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSInvocation getArgument:atIndex:]: index (2) out of bounds [-1, 1]'` 的错误
|
||||
|
||||
<img src="./../assets/RuntimeMethodForwardingDemo2.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RuntimeMethodForwardingDemo2.png" style="zoom:30%" />
|
||||
|
||||
|
||||
|
||||
@@ -3124,7 +3124,7 @@ Person 类不存在对象方法 makeliving ,PersonHelper 类存在。
|
||||
|
||||
通过 `NSInvocation` 获取参数一般是从2开始,因为第一个是 self,第二个是 _cmd,第三个是 cost
|
||||
|
||||
<img src="./../assets/RuntimeMethodForwardingWithMethodSignature.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RuntimeMethodForwardingWithMethodSignature.png" style="zoom:30%" />
|
||||
|
||||
|
||||
|
||||
@@ -3151,11 +3151,11 @@ Person 类不存在对象方法 makeliving ,PersonHelper 类存在。
|
||||
}
|
||||
```
|
||||
|
||||
<img src="./../assets/RuntimeMethodForwardingByClassMethod.png" style="zoom:30%"/>
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RuntimeMethodForwardingByClassMethod.png" style="zoom:30%"/>
|
||||
|
||||
|
||||
|
||||
<img src="./../assets/RuntimeMethodForwardingByClassMethod2.png" style="zoom:30%"/>
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RuntimeMethodForwardingByClassMethod2.png" style="zoom:30%"/>
|
||||
|
||||
注意:**实例对象有消息转发,类方法也有消息转发机制**。但是在 Xcode 中只可以提示 `-(id)forwardingTargetForSelector:(SEL)aSelector`
|
||||
|
||||
@@ -3184,7 +3184,7 @@ OC 中方法调用的本质就是利用 runtime 发消息,发消息也给 rece
|
||||
|
||||
3. 如果在当前类的方法列表中没有找对方法实现,则根据类对象的 superclass 在父类的类对象的方法列表中继续查找
|
||||
|
||||
<img src="./../assets/MethodCacheLookUpProcess.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/MethodCacheLookUpProcess.png" style="zoom:30%" />
|
||||
|
||||
先根据 superclass 找到父类,然后在父类中也按照上面3点进行递归查找,即:
|
||||
|
||||
@@ -3311,7 +3311,7 @@ objc_msgSendSuper(arg, sel_registerName("class"))
|
||||
|
||||
我们对 iOS 项目`[super viewDidLoad]` 下符号断点,发现`objc_msgSendSuper2`
|
||||
|
||||

|
||||

|
||||
|
||||
查看 objc4 源代码发现是一段汇编实现。
|
||||
|
||||
@@ -3399,7 +3399,7 @@ call - 调用函数
|
||||
|
||||
也可以在 Xcode 上可视化面板操作,路径为:菜单栏 `Product -> Perform Action -> Assemble "ViewController.m"`
|
||||
|
||||
<img src="./../assets/XcodeAssembleClass.png" style="zoom:30%">
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/XcodeAssembleClass.png" style="zoom:30%">
|
||||
|
||||
|
||||
|
||||
@@ -3425,7 +3425,7 @@ call - 调用函数
|
||||
|
||||
Demo1
|
||||
|
||||
<img src="./../assets/RuntimeIsKindOfClassDemo1.png" style="zoom:40%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RuntimeIsKindOfClassDemo1.png" style="zoom:40%" />
|
||||
|
||||
|
||||
|
||||
@@ -3453,7 +3453,7 @@ Demo1
|
||||
|
||||
Demo2
|
||||
|
||||
<img src="./../assets/RuntimeIsKindOfClassDemo2.png" style="zoom:100%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RuntimeIsKindOfClassDemo2.png" style="zoom:100%" />
|
||||
|
||||
下面面2个判断都是调用类方法的 `isMemberOfClass` 、`isKindOfClass`
|
||||
|
||||
@@ -3515,7 +3515,7 @@ QA:`NSLog(@"%d", [Person isKindOfClass:[NSObject class]]);` 为什么输出1
|
||||
- 开启 for 循环,判断 tcls 是否等于方法参数(也就是 NSObject 的类对象)
|
||||
- for 循环一开始不满足,则不断进行,令 tcls = tcls->superClass。for 循环结束的条件是 tcls 为 nil,所以最后找到 NSObject 的元类的时候,继续往上找,NSObject 元类对象的父类是 NSObject 的类对象,此时 tcls 就是 NSObject 的类对象,cls 也是 NSObject 类的类对象,相等,输出1.
|
||||
|
||||
<img src="./../assets/class-isa-superclass.png" style="zoom:35%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/class-isa-superclass.png" style="zoom:35%" />
|
||||
|
||||
同理 `[NSObject isKindOfClass:[NSObject class]]` 也为 YES,工作流程和上面的类似。也就是 `[继承自 NSObject 及其继承自任何子类 isKindOfClass:[NSObject class]]` 都为 YES
|
||||
|
||||
@@ -3523,7 +3523,7 @@ QA:`NSLog(@"%d", [Person isKindOfClass:[NSObject class]]);` 为什么输出1
|
||||
|
||||
|
||||
|
||||
<img src="./../assets/RuntimeIsKindOfClassUnitDemo.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RuntimeIsKindOfClassUnitDemo.png" style="zoom:30%" />
|
||||
|
||||
|
||||
|
||||
@@ -3560,7 +3560,7 @@ QA:`NSLog(@"%d", [Person isKindOfClass:[NSObject class]]);` 为什么输出1
|
||||
|
||||
程序运行什么结果?
|
||||
|
||||
<img src="./../assets/RuntimeQA1.png" style="zoom:30%">
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RuntimeQA1.png" style="zoom:30%">
|
||||
|
||||
为什么会方法调用成功?为什么 name 打印出为 @"杭城小刘"。我们来分析下:
|
||||
|
||||
@@ -3594,7 +3594,7 @@ void test () {
|
||||
|
||||
方法内的变量存储在栈上,堆向上增长,栈向下增长。
|
||||
|
||||

|
||||

|
||||
|
||||
第三,id 的本质是
|
||||
|
||||
@@ -3645,7 +3645,7 @@ struct Person_IMPL {
|
||||
|
||||
再看一个变体1
|
||||
|
||||
<img src="./../assets/RuntimeQA2.png" style="zoom:30%">
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RuntimeQA2.png" style="zoom:30%">
|
||||
|
||||
打印输出是因为 `*p` 类似 isa 指针。本身占用8字节空间,然后访问 `self->_name` 就是 `base + 8 = isa地址 + 8 ` 出的内存就是 name,`*p` 是在栈中,加8,就是向上声明的变量,当前情况下 `Address(*p) + 8` 就是 temp 变量。所以输出 `<NSObject: 0x****>`
|
||||
|
||||
@@ -3653,7 +3653,7 @@ struct Person_IMPL {
|
||||
|
||||
再看一个变体2
|
||||
|
||||
<img src="./../assets/RuntimeQA3.png" style="zoom:30%">
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RuntimeQA3.png" style="zoom:30%">
|
||||
|
||||
分析: 搞懂的小伙伴不迷惑了。没搞懂其实就是没搞懂**栈地址由高到低,向下生长** 和 `super` 调用的本质。
|
||||
|
||||
@@ -3672,7 +3672,7 @@ objc_msgSendSuper(arg, sel_registerName("viewDidLoad"));
|
||||
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
可能会疑问,知道了 super 调用本质,知道了会产生一个局部变量的结构体,但是结构体里面2个成员变量,找属性的时候,isa 下的8个字节会命中哪个?
|
||||
|
||||
@@ -3691,7 +3691,7 @@ struct objc_super {
|
||||
|
||||
|
||||
|
||||
<img src="./../assets/RuntimeQA.png" style="zoom: 80%">
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RuntimeQA.png" style="zoom: 80%">
|
||||
|
||||
|
||||
|
||||
@@ -3706,7 +3706,7 @@ id rs = [NSObject valueForKey:@"isa"];
|
||||
NSLog(@"%@", rs);
|
||||
```
|
||||
|
||||
<img src="./../assets/RuntimeCallValueForKeyOnObject.png" style="zoom:50%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RuntimeCallValueForKeyOnObject.png" style="zoom:50%" />
|
||||
|
||||
不会 Crash。输出的是 NSObject 的元类对象。因为 NSObject 调用的是类方法,先去元类对象的方法列表中查找,发现没有 `valueForKey` 方法。则继续从 NSObject 元类对象的父类对象,也就是 NSObject 的类对象上查找 `valueForKey` 方法。
|
||||
|
||||
@@ -3716,7 +3716,7 @@ NSLog(@"%@", rs);
|
||||
|
||||
查看了 objc 源码,会发现很多 NSObject 的基础方法:`+ (id)init`、`- (id)init` 等均有 `+` 、`-` 方法。
|
||||
|
||||
<img src="./../assets/NSObjectImpleByClassAndInstance.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/NSObjectImpleByClassAndInstance.png" style="zoom:30%" />
|
||||
|
||||
为什么这么设计?猜测是为了代码的健壮。
|
||||
|
||||
@@ -3767,7 +3767,7 @@ Person *p = [Person new];
|
||||
object_setClass(p, [Student class]);
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
@@ -3796,7 +3796,7 @@ void createClass (void) {
|
||||
}
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
注意:
|
||||
|
||||
@@ -3906,7 +3906,7 @@ free(properties);
|
||||
|
||||
|
||||
|
||||
<img src="./../assets/RuntimeDicToObject.png" style="zoom:30%" />
|
||||
<img src="https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/RuntimeDicToObject.png" style="zoom:30%" />
|
||||
|
||||
不够健壮体现在:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user