[toc]
面試題
先切入一個面試問題, 下面代碼會輸出什么?
// Student: Person: NSObject
- (instancetype)init {
if (self = [super init]) {
NSLog(@"[self class] = %@", [self class]); // Student
NSLog(@"[self superclass] = %@", [self superclass]); // Person
NSLog(@"--------------------------------");
// objc_msgSendSuper({self, [Person class]}, @selector(class));
// ({消息接收者, 消息接收者父類}, SEL)
NSLog(@"[super class] = %@", [super class]); // Student
NSLog(@"[super superclass] = %@", [super superclass]); // Person
}
return self;
}
底層分析
OC代碼
// Student: Person: NSObject
- (void)run {
[super run]; // super調(diào)用的消息接收者仍然是 self (Student實例)
}
轉(zhuǎn)成C++代碼
static void _I_Student_run(Student * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("run"));
}
// objc4/message.h
id _Nullable objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
上面代碼拆解
struct __rw_objc_super rcv = {
self,
class_getSuperclass(objc_getClass("Student"))
}
objc_msgSendSuper(rcv, @selector(run))
objc_super
__rw_objc_super
等價于 objc_super
// objc4/message.h
/// Specifies the superclass of an instance.
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained _Nonnull id receiver; // 消息接收者
__unsafe_unretained _Nonnull Class super_class; // 消息接收者的父類
/* super_class is the first class to search */
};
class_getSuperclass()
// objc4/runtime.h
Class _Nullable class_getSuperclass(Class _Nullable cls)
// objc4/objc-class.mm
Class class_getSuperclass(Class cls) {
if (!cls) return nil;
return cls->superclass;
}
objc_msgSendSuper()
可以看到, 使用 super 調(diào)用方法, 底層被轉(zhuǎn)換成 objc_msgSendSuper()
/**
* Sends a message with a simple return value to the superclass of an instance of a class.
*
* @param super A pointer to an \c objc_super data structure. Pass values identifying the
* context the message was sent to, including the instance of the class that is to receive the
* message and the superclass at which to start searching for the method implementation. ★★
* @param op A pointer of type SEL. Pass the selector of the method that will handle the message.
* @param ...
* A variable argument list containing the arguments to the method.
*
* @return The return value of the method identified by \e op.
*
* @see objc_msgSend
*/
// super 里封裝了消息接收者(方法調(diào)用者) 和 消息接收者的父類
id _Nullable objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
蘋果注釋說, superclass 的作用是從哪里開始查找方法實現(xiàn); 即從 objc_super.super_class 開始查找,
所以, super 調(diào)用會直接從父類開始查找方法的實現(xiàn)
[super funcName];
而super調(diào)用的消息接收者仍然是子類對象, 只不過是從父類從開始查找方法的實現(xiàn)
相當于還是self在調(diào)用方法
objc_msgSendSuper2
★
而打斷點分析匯編, 可以看到, 實際真正調(diào)用的是 objc_msgSendSuper2
// objc4/objc-abi.h
// objc_msgSendSuper2() takes the current search class, not its superclass.
id _Nullable objc_msgSendSuper2(struct objc_super * _Nonnull super, SEL _Nonnull op, ...);
struct objc_super2 {
id receiver; // 消息接收者
Class current_class; // 當前類, 即receiver的Class對象
};
匯編實現(xiàn)
該方法會拿到當前類, 在內(nèi)部根據(jù)class找到superclass, 然后開始查找方法實現(xiàn), 實際效果和 objc_msgSendSuper 是一樣的
ENTRY _objc_msgSendSuper2
UNWIND _objc_msgSendSuper2, NoFrame
ldp p0, p16, [x0] // p0 = real receiver, p16 = class
ldr p16, [x16, #SUPERCLASS] // p16 = class->superclass
CacheLookup NORMAL, _objc_msgSendSuper2
END_ENTRY _objc_msgSendSuper2
class 和 superclass 方法
每個OC類都有class方法, 可想而知, class 的方法實現(xiàn)在 NSObject 上, 所以不管 self調(diào)用, 還是super調(diào)用, 最終都是在 NSObject 上找到
// objc4/NSObject.mm
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
+ (Class)superclass {
return self->superclass;
}
// class_getSuperclass(object_getClass(self));
- (Class)superclass {
return [self class]->superclass;
}