五:消息转发流程:objc_msgSend

本节知识重点:

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的流程 !!!!!

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容