iOS self和super的異同

「大師,今天小師妹從一本古籍中發現一些祕術,並以此問了我一個問題,可我並不知道,這下要在小師妹那裏丟人了..」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

可使用命令:指針

  • clang -rewrite-objc filename.m
  • xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc filename.m -o output.cpp

第二種方法過濾了平臺以及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_msgSendobjc_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表明了從父類開始尋找方法。

至此,selfsuper這兩個的區別已經明瞭:

  • [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
複製代碼
  • 在給category綁定屬性時,能夠用_cmd來做爲惟一的key,由於selector是一個惟一的字符串
- (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同樣。

「以上就是題目的來龍去脈,以及默認參數的使用方法。」

「大師一番話讓小生豁然開朗,多謝大師。」

「少俠快去找小師妹去罷。」

相關文章
相關標籤/搜索