objc_msgSend

156 阅读2分钟
@interface Person : NSObject
- (void)say;
- (void)say2;
@end
@implementation Person
- (void)say{
    NSLog(@"Person: %s",__func__);
}
- (void)say2{
    NSLog(@"Person: %s",__func__);
}
@end

@interface Teacher : Person
- (void)say;
//Teacher 只声明say2,不去实现say2
- (void)say2;
@end
@implementation Teacher
- (void)say{
    NSLog(@"Teacher: %s",__func__);
}
@end

OC方法的本质

Person *person = [[Person alloc]init];
[person say];
// 将上面代码转为cpp文件后,得到下面代码
 Person *person = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
 
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("say"));

通过上述代码可以看出,方法的本质就是objc_msgSend消息发送 我们也可以进行一下验证

Person *person = [[Person alloc]init];
objc_msgSend(person, sel_registerName("say"));

和上面得到一样的输出

objc_msgSend & objc_msgSendSuper

  • objc_msgSend
//第一个参数:消息接收者
//第二个参数:消息的主体(sel 方法名 + argv 方法参数)
objc_msgSend(<#id  _Nullable self#>, <#SEL  _Nonnull op, ...#>)

Person *person = [[Person alloc]init];
objc_msgSend(person,sel_registerName("say"));
  • objc_msgSendSuper
//第一个参数:结构体
//第二个参数:sel
objc_msgSendSuper(<#struct objc_super * _Nonnull super#>, <#SEL  _Nonnull op, ...#>)
//objc_super 定义如下由receiver和super_class
objc_super{
    __unsafe_unretained _Nonnull id receiver;
    __unsafe_unretained _Nonnull Class super_class;
}
Person *person = [[Person alloc]init];
Teacher *techer = [[Teacher alloc]init];
struct objc_super mr_objc_super;
mr_objc_super.receiver = techer;
mr_objc_super.super_class = Person.class;
objc_msgSendSuper(&mr_objc_super, sel_registerName("say2")); 
//输出为:Person: -[Person say2]

通过上面代码我们可以得出结论:方法调用,首先是在类中查找,如果类中没有找到,会到类的父类中查找。 我们看一道题,Teacher继承自Person Xnip2022-02-04_01-40-33.jpg 我们来看下Teacher init函数中的输出, [self class] 应该没有任何争议,输出Teacher 但是 [super class]为什么输出的也是Teacher呢? 我们将上述代码转化为cpp文件,摘出来关键代码 Xnip2022-02-04_01-46-01.jpg

  • [self class] 调用了 objc_msgSend
  • [super class] 调用了 objc_msgSendSuper 上面已经讲了objc_msgSendSuper,在这里receiver 还是Teacher 所以输出的还是Teacher

当遇到方法调用时,编译器生成对objc_msgSend、objc_msgSendSuper函数之一的调用。发送到对象的超类(使用super关键字)的消息使用objc_msgSendSuper发送;其他消息使用objc_msgSend发送。

方法查找

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
}
struct cache_t {
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask; // 8
    explicit_atomic<mask_t>    _maybeMask; // 4
    uint16_t                   _flags;  // 2
    uint16_t                   _occupied; // 2
}
struct bucket_t {
    // IMP-first is better for arm64e ptrauth and no worse for arm64.
    // SEL-first is better for armv7* and i386 and x86_64.
#if __arm64__
    explicit_atomic<uintptr_t> _imp;
    explicit_atomic<SEL> _sel;
#else
    explicit_atomic<SEL> _sel;
    explicit_atomic<uintptr_t> _imp;
#endif
}

由上面的数据结构我们可以猜测出,cache中缓存的是sel-imp 所以方法的查找可以理解为从cache中查找sel-imp