写在前面: iOS底层原理探究是本人在平时的开发和学习中不断积累的一段进阶之
路的。 记录我的不断探索之旅,希望能有帮助到各位读者朋友。
目录如下:
- iOS 底层原理探索 之 alloc
- iOS 底层原理探索 之 结构体内存对齐
- iOS 底层原理探索 之 对象的本质 & isa的底层实现
- iOS 底层原理探索 之 isa - 类的底层原理结构(上)
- iOS 底层原理探索 之 isa - 类的底层原理结构(中)
以上内容的总结专栏
细枝末节整理
写在前面
在之前的系列文章中,我们从开发中使用最多和最基础的 alloc 开始,探索了其底层的流程,以及类底层结构体的内存知识,接着关于对象的本质和其内部isa的底层实现,进一步的,我们探索了类的bits和cache。
那么,本片作为类的底层原理结构的收官内容,我会从整体来总结下。
准备
来自苹果WWDC2020关于 Objective-C 运行时做出的更改
深入到每个Objective-C和Swift类背后的低级位元和字节的微观世界。
了解最近对内部数据结构、方法列表和标记指针的更改如何提供更好的性能
和更低的内存使用。我们将演示如何识别和修复取决于内部细节的代码崩溃,
并向您展示如何保持代码不受运行时更改的影响。
clean memory是指加载后不会发生改变的内存。(class_ro_t 就属于 clean memory 因为他是只读的)dirty memory是指在进程运行时会发生更改的内存。(类结构一经使用就会变成 dirty memory,因为运行时会向他写入新的数据,例如 创建一个新的方法缓存并从类中指向它)dirty memory比clean memory昂贵得多。它必须在进程运行期间一直存在。另一方面,可以排除clean memory,为其他东西腾出空间,因为如果您需要它,系统总是可以从磁盘重新加载它。macOS有交换dirty memory的选项,但dirty memory在iOS中特别昂贵,因为它不使用交换。
类
磁盘上app二进制文件中的类
它包含最频繁访问的信息:指向元类、超类和方法缓存的指针。
当类第一次从磁盘加在到内存时的结构
它还有一个指针,指向存储其他信息的更多数据,称为class_ro_t
Ro代表只读。这包括类的名称和关于方法、协议和实例变量的信息。Swift类和Objective-C类共享这个基础设施,所以每个Swift类也有这些数据结构。
当类第一次被使用时
当一个类第一次被使用时,运行时将为它分配额外的存储空间。
这个运行时分配的存储是class_rw_t,用于读/写数据。
在这个数据结构中,我们存储只在运行时生成的新信息。当一个类别被加载时,它可以向类中添加新方法,程序员可以使用运行时api动态地添加它们。
因为class_ro_t是只读的,所以我们需要跟踪class_rw_t中的这些内容。
类的整体结构
将通常不使用的部件分开,这样 class_rw_t 的大小减少了一半
cache_t 的 insert() 调用的时机
通过源码断点调试,我们在堆栈信息中可以找到
log_and_fill_cache
lookUpImpOrForward
这就是,之后我们要去探索的关于消息发送转发和查找的相关流程内容。留着我们之后的文章展开探索。
补充
-
面试题
源码分析
我们可以从源码中找到方法的实现:
分析可知:
-
类的
isKindOfClass方法 是 先根据isa找到元类, 做比较, 然后找到元类的父类 ,再做比较,循环下去, 只要跟着isa可以找到元类就比较,只要有一次可以比对上,就返回 YES。 -
类的
isMemberOfClass方法 是 看自己的元类 和 要比较的类是否相等; -
实例对象的
isKindOfClass方法 是 看自己的类是否和方法传进来的参数相同; -
实例对象的
isMemberOfClass方法 是 看自己的类 和 要比较的类是否相等;
- 对于
t1而言,NSObject class的isa是NSObject MateClass,NSObject MateClass的superClass是NSObject class所以, 返回 YES; - 对于
t2而言,SMPerson class的isa是SMPerson Mateclass,不等于SMPerson class,SMPerson Mateclass的superclass是NSObject MateClass,不等于SMPerson class, 所以返回 NO; - 对于
t3而言,NSObject class的isa是NSObject MateClass, 不等于NSObject class, 所以返回 NO; - 对于
t4而言,SMPerson class的isa是SMPerson MateClass, 不等于SMPerson class,所以返回NO。 - 对于
t5而言,objc的isa是NSObject class,等于NSObject class, 所以是 YES; - 对于
t6而言objc的isa是SMPerson class, 所以返回 YES; - 对于
t7而言,objc的isa是NSObjcet class, 所以返回 YES; - 对于
t8而言,objc的isa是SMPersn class, 所以返回YES。
输出如下, 与我们根据源码分析的一样。
1 - 0 - 0 - 0 - 1 - 1 - 1 - 1
LLDB调试
是否真的如我们分析的过程一致呢?接下来我们开始LLDB调试验证过程:
如上图所示,你也看到了,我要通过断点代码调试来展开分析一下内容:
熟悉的 debug workdfllow -> always show disassembly,
打断点发现 isMemberOfClass 方法走了断点, isKindOfClass 没有走断点。
发现系统会先调用 objc_opt_class 的方法来, 之后 去到 objc_opt_isKindOfClass方法, 那么,打开我们的源码工程查找看看:
objc_opt_class 方法 获取 传入参数的 isa 指向的类, 如果 isa 指向的类是元类,则返回这个对象,否则返回isa指向的类; 也就是说,实例 调用此方法,返回 实例的类对象; 类 调用此方法 返回它自己;
接着再看;
方法会拿到 obj 的 isa 指向的类 cls , 如果 cls 存在,判断是否等于 otherClass , 等于 则 返回YES, 否则,去找到 cls 的父类, 接着判断是否等于 otherClass, 如此 做一个 for循环,直到最后没有匹配上之后,返回NO。