docs: image url

This commit is contained in:
FantasticLBP
2026-01-02 10:28:57 +08:00
parent 7ac7513900
commit 7843661458
29 changed files with 719 additions and 719 deletions

View File

@@ -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
可以对照下面的表格进行查看:
![](./../assets/runtime-method-encoding.png)
![](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/runtime-method-encoding.png)
```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 崩溃窥探一二
![](./../assets/runtime-forwardingFailed.png)
![](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/runtime-forwardingFailed.png)
```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`
![](./../assets/runtime-super.png)
![](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/runtime-super.png)
查看 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 () {
方法内的变量存储在栈上,堆向上增长,栈向下增长。
![](./../assets/runtime-isa-demo.png)
![](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/runtime-isa-demo.png)
第三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"));
![](./../assets/runtime-super-isa-demo.png)
![](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/runtime-super-isa-demo.png)
可能会疑问,知道了 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]);
```
![](./../assets/runtime-changeisa-demo.png)
![](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/runtime-changeisa-demo.png)
@@ -3796,7 +3796,7 @@ void createClass (void) {
}
```
![](./../assets/runtime-dynamicCreateClass-demo.png)
![](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/runtime-dynamicCreateClass-demo.png)
注意:
@@ -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%" />
不够健壮体现在: