「大師,今天小師妹從一本古籍中發現一些祕術,並以此問了我一個問題,可我並不知道,這下要在小師妹那裏丟人了..」bash
「少俠莫慌,先且把題目示於老夫。」架構
「題目是這樣」app
在一個繼承於NSObject的類中,調用[self class]
和[super class]
,結果分別是什麼?iphone
@interface Person : NSObject
@end
@implementation Person
- (instancetype)init
{
self = [super init];
if (self) {
NSLog(@"Person self: %@", [self class]);
NSLog(@"Person super: %@", [super class]);
}
return self;
}
@end
複製代碼
「少俠,看好了,答案是這樣。」ide
Person self: Person
Person super: Person
複製代碼
「大師,爲何是這樣呢,[super class]
不該該打印出NSObject
嗎?」ui
「少俠,且聽老夫細細道來。」spa
使用clang
編譯器將上面使用的OC文件,編譯成C++文件,來看一下源碼。3d
可使用命令:指針
第二種方法過濾了平臺以及CPU架構,因此編譯成C++文件以後,代碼量會更少一些。code
在編譯以後的C++文件中,找到init方法。
static instancetype _I_Person_init(Person * self, SEL _cmd) {
self = ((Person *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person"))}, sel_registerName("init"));
if (self) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_x9_w2_m5ffn49j8y3805_0sq6bm0000gn_T_Person_29eaed_mi_0, ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class")));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_x9_w2_m5ffn49j8y3805_0sq6bm0000gn_T_Person_29eaed_mi_1, ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person"))}, sel_registerName("class")));
}
return self;
}
複製代碼
在這裏面,找到了兩個重要的方法。
[super class]
和[self class]
的不一樣,就在於objc_msgSend
和objc_msgSendSuper
的不一樣。
接下來,咱們打開runtime
的源碼,查看一下這兩個方法作了什麼。runtime
的源碼在這裏下載
下面來看一下這兩個方法:
id objc_msgSend(id self, SEL op, ...)
self
A pointer that points to the instance of the class that is to receive the message.
用來接收消息的當前類的實例
op
The selector of the method that handles the message.
當前方法的selector
...
A variable argument list containing the arguments to the method.
複製代碼
self是當前類的一個實例,用來接收方法。 op是方法,在此例中是class
方法。
id objc_msgSendSuper(struct objc_super *super, SEL op, ...);
Sends a message with a simple return value to the superclass of an instance of a class.
super
A pointer to an 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.
一個指向objc_super的指針
op
A pointer of type SEL. Pass the selector of the method that will handle the message.
當前方法的selector
...
A variable argument list containing the arguments to the method.
複製代碼
objc_super
是結構體,結構體結構以下:
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained _Nonnull id receiver;
/// Specifies the particular superclass of the instance to message.
#if !defined(__cplusplus) && !__OBJC2__
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained _Nonnull Class class;
#else
__unsafe_unretained _Nonnull Class super_class;
#endif
/* super_class is the first class to search */
};
複製代碼
簡化一下就是:
struct objc_super {
id receiver;
Class super_class;
};
複製代碼
objc_super
結構體裏面,包含了當前類的對象做爲消息接收者,super_class
表明了從父類開始尋找方法。
至此,self
、super
這兩個的區別已經明瞭:
[self msg]
從當前類中開始尋找方法,尋找不到父類中尋找。[super msg]
從父類中開始尋找方法。[self msg]
和[super msg]
中的消息接收者,均爲當前類的對象。在此例中,[receiver class]
方法,在NSObject
類中,因此從Person
中尋找和從NSObject
中尋找的結果同樣,消息的接收者又都是Person
的實例,因此產生了開頭的結果。
另從編譯以後的代碼中,能夠看到方法都自帶兩個隱藏的參數,一個是self,一個是cmd。
self表明當前類的對象 cmd表明當前方法的selector
cmd能夠有如下做用:
- (void)test
{
NSLog(@"call: %@", NSStringFromSelector(_cmd));
}
複製代碼
打印出:
call: test
複製代碼
- (CustomNavigationControllerDelegate *)customDelegate
{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setCustomDelegate:(CustomNavigationControllerDelegate *)customDelegate
{
objc_setAssociatedObject(self, @selector(customDelegate), customDelegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
複製代碼
爲何getter方法裏面用_cmd,可是setter方法裏用@selector(customDelegate)
,由於_cmd表明的是當前方法,在getter裏面時和在setter裏面時,是不同的。做爲key,兩個方法裏同樣。使用上面的方法就確保了兩個地方的key同樣。
「以上就是題目的來龍去脈,以及默認參數的使用方法。」
「大師一番話讓小生豁然開朗,多謝大師。」
「少俠快去找小師妹去罷。」