一. objc_object结构体 objc_class结构体
1.1 objc_object结构体
image.png image1.png- isa指针分为
指针型isa
和非指针型isa
1.2 objc_class结构体
结构体主要内容:
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() {
return bits.data();
}
......
}
image.png
1.3 cache_t结构体
struct cache_t {
struct bucket_t *_buckets;//可以理解为一个数组
mask_t _mask;
mask_t _occupied;
......
}
方法缓存的数据结构.png
通过哈希查找.png
image.png
cache_t的特点:
1.用于快速
查找方法执行函数
2.是可增量扩展
的哈希表
结构(增量拓展:结构的量增大,内存也会增大)
3.是局部性原理
的最佳应用(局部性原理:调用次数较高的方法)
1.4 class_data_bits_t结构体
class_data_bits_t包含class_rw_t,class_rw_t内包含
struct class_rw_t {
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
......
}
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
image.png
- 一个类定义的变量 方法 属性都在class_data_bits_t这个成员结构内
protocols(协议) properties(属性) methods(方法) 是一个二维数组,还包括一些分类等
image.pngname 类名
ivars 声明,定义的成员变量
properties 属性
protocols 协议
methods 方法
他们是一个一维数组
二.类对象 元类对象
类对象
存储实例方法列表等信息
元类对象
存储类方法列表等信息
问题1:
类对象和元类对象之间有什么区别和联系?
实例对象通过isa指针找到类对象
类对象当中存储方法列表等信息
类对象通过isa指针找到元类对象、从而可以访问类方法列表等相关信息
类对象和元类对象都是objc_class数据结构,因为继承objc_object。所以他们才有isa指针,进而可以实现实例对象通过isa指针找到类对象,进而可以访问实例方法列表等信息。类对象通过isa指针找到元类对象、从而可以访问类方法列表的相关信息。
问题2:
如果我们调用的类方法,没有对应的实现。但是有同名的实例方法,会不会发生崩溃,会不会产生实际的调用?
由于根元类对象的superclass指针,指向了根类对象,当我们在元类对象当中查找类方法列表查不到的时候。会顺着指针去实例方法列表查找。如果有同名方法,就会调用同名方法的实例方法调用。
问题3:
实例方法的传递过程?
系统根据当前的isa指针。找到它的类对象,在它的类对象当中查找方法列表。没有查找到的话,就会根据superclass查找父类的类对象的方法列表。然后根类对象的方法列表,如果还没有,就进入消息转发流程。
问题4:
类方法的传递过程?
通过类对象的isa指针,找到元类对象。在它的元类对象当中查找方法列表。没有查找到的话,就会根据superclass查找父类的元类对象的方法列表。然后顺次遍历方法列表,直到根元类对象,再到根类对象。然后根类对象的方法列表,如果还没有,就进入消息转发流程。
问题4:
类对象 元类对象isa指向问题。
元类对象的isa指针 指向 根元类对象
根元类对象isa指针 指向 根元类对象
根元类对象的superclass指针指向根类对象
问题5:
打印结果
结果都是Phone。因为接收者都是当前对象。
image.png
问题6:
消息传递的机制?
首先通过 哈希查找 查找缓存,缓存没命中,查当前类的方法列表是否命中(对于已排序好的列表,采用二分查找,对于没有排序的列表,采用一般遍历查找方法)。如果仍然没命中,逐级父类方法列表是否命中(父类查找是先通过superclass指针查找父类,遍历每一个父类,对于每一个父类,又通过缓存查找和当前类方法列表查找)。如果都没有命中,进入消息转发流程。
问题7:
[obj foo]和objc_msgSend()函数之间有什么关系?
编译之后,会在内部变成objc_msgSend()函数调用。
问题8:
消息转发流程3个步骤
1.resolvelnstanceMethod(告诉系统是否要解决当前的方法实现)
2.forwardingTargetForSelector:(告诉系统这个实例方法的调用应该由哪个对象来处理,转发对象是谁)
3.methodSignatureForSelector:(1.返回方法签名,调用forwardInvocation,如果可以处理,转发结束 2.如果返回为nil,或者forwardInvocation无法处理,无法找到方法崩溃)
问题9:
runtime如何通过Selector找到对应的IMP地址的?
首先查找当前实例对应类对象的缓存是否有Selector对应的IMP实现,如果缓存命中,就把命中的缓存函数返回给调用方。如果缓存没有命中。根据当前类的方法列表查找Selector对应的IMP事件。当前类如果没有命中,再根据当前类的superClass指针逐级查找父类的方法列表。然后查找Selector对应的IMP事件。
问题10:
@dynamic (动态方法解析)
相当于在运行时添加set get方法。而不是在编译时声明好
动态运行时语言将函数决议推迟到运行时
编译时语言在编译期进行函数决议
网友评论