@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
我们来看下Teacher init函数中的输出, [self class] 应该没有任何争议,输出Teacher
但是 [super class]为什么输出的也是Teacher呢? 我们将上述代码转化为cpp文件,摘出来关键代码
[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