本节知识重点:
objc_msgSend : 消息转发流程
objc_msgSend(void /* id self, SEL op, ... */ )
objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
简单实现:
HJFood *food = [HJFood alloc];
objc_msgSend(food,sel_registerName("sayMyName"));
[food sayMyName];
objc_msgSend(food,sel_registerName("sayMyName"))
等价于 [food sayMyName]
** OC层面的代码实现均可以用运行时objc底层代码来实现
跳过自身本类,实现父类的重写方法:
@interface HJFood : NSObject
- (void)sayMyName;
@end
@implementation HJFood
- (void)sayMyName{
NSLog(@"我是食物");
}
@end
@interface HJBanana : HJFood
- (void)sayMyName;
@end
@implementation HJBanana
- (void)sayMyName{
NSLog(@"我是香蕉");
}
@end
如果 Bannana 类调用已经重写的“sayMyName”方法,会输出自己的名字,但是此时我需要直接实现父类的输出,这时候可以进行 “运行时”来实现
HJBanana *banana = [HJBanana alloc];
struct objc_super stru_banana;
banana.receiver = banana;
stru_banana.super_class = [HJFood class];
objc_msgSendSuper(&stru_banana, sel_registerName("sayMyName"));
引申:
imp:函数指针地址。
注意:只要我们能拿到imp就能找到对应的函数内容,那么我们是如何通过方法来找到对应的消息体呢(sel 、imp)呢,这是我们要学习的重点
分析:也就是我们是如何根据方法(sayHello),来找到函数imp的呢?????
探索:
问题抛出:为什么objc_msgSend(接受者, sel)中要有消息接受者呢???
接受者的作用是:?????
论证:
1.消息接收者(实例化对象objc) -> ISA : 首先我们得知对象中有ISA,ISA_t中存在Class的类信息
2.ISA -> Class : 得到我们的类信息
3. Class -> Cache :类中存储着我们的cache缓存信息
4. Cache -> Buckets :缓存信息中存储着着buckets信息
5. Buckets -> (sel 、 imp) :查找与cmd匹配的sel,找到直接返回;未找到跳到“6”
6. Class -> (class_data_bits_t*)bits :类中存储着我们的bits信息
7. bits -> (class_rw_t*)data :找到data信息
8.` (class_rw_t*)data -> methods : 找到对象方法,或者从元类中找类方法
9.......未完待续
重点探究:如何从cache
中查找到对应的bucket(sel 、imp)
[bannana sayHello] -> imp ( cache -> bucket (sel imp))
// 获取当前的对象
id bannana = 0x10000
// 获取isa
isa_t isa = 0x000000
// isa -> class -> cache
cache_t cache = isa + 16字节
// arm64
// mask|buckets 在一起的
buckets = cache & 0x0000ffffffffffff
// 获取mask
mask = cache LSR #48
// 下标 = mask & sel
index = mask & p1
// bucket 从 buckets 遍历的开始 (起始查询的bucket)
bucket = buckets + index * 16 (sel imp = 16)
int count = 0
// CheckMiss $0
do{
if ((bucket == buckets) && (count == 0)){ // 进入第二层判断
// bucket == 第一个元素
// bucket人为设置到最后一个元素
bucket = buckets + mask * 16
count++;
}else if (count == 1) goto CheckMiss
// {imp, sel} = *--bucket
// 缓存的查找的顺序是: 向前查找
bucket--;
imp = bucket.imp;
sel = bucket.sel;
}while (bucket.sel != _cmd) // // bucket里面的sel 是否匹配_cmd
// CacheHit $0
return imp
CheckMiss:
CheckMiss(normal)
以上就是我们如何从cache中获取对应的imp的流程 !!!!!