ios 中 [self class] 和 [super class] 區(qū)別

問題?

網(wǎng)上很多關于[self class][super class] 的討論,討論問題的焦點是為什么[self class][super class] 輸出的結果是一樣的,即都在Son類中輸出的話,都輸出Son。網(wǎng)上大多都很雷同,點都說到了但沒有指明關鍵點,怎么自己一步一步的去找到問題的根源,只是填鴨式的告訴讀者。感覺很??...

解決方案(一步步找到問題)

第一步 創(chuàng)建兩個類Person和Son(情景重現(xiàn))
#import <Foundation/Foundation.h>

@interface Parent : NSObject

@end

@implementation Parent

@end

@interface Son : Parent

@end

@implementation Son

- (instancetype)init{
    if (self = [super init]) {
        NSLog(@"self: %@",[self class]);
        NSLog(@"super: %@",[super class]);
    }
    return self;
}

@end

上述代碼調(diào)用Son 類的 init 方法后,輸出如下

2018-02-12 11:11:19.984281 testProject[434:111502] self: Son
2018-02-12 11:11:19.984387 testProject[434:111502] super: Son
第二步 我們可以看一下代碼真實調(diào)用情況....

看第二步之前先了解一下selfsuper的區(qū)別

  1. self 是當前方法的調(diào)用者,是方法的隱藏參數(shù),方法的隱藏參數(shù)還有一個_cmd參數(shù),可以在調(diào)試的時候看到。
    image.png

如果是類方法:代表當前類
如果是對象方法:代表當前類的對象

  1. super 是編譯器指令

把上述代碼寫到一個文件中,命名為Parent.m文件,放到桌面的clang文件夾中


image.png

打開終端,執(zhí)行命令

  1. 先cd到Parent.m目錄中
  2. 在執(zhí)行clang -rewrite-objc Parent.m命令
    image.png

    這樣在clang目錄中可以看到多了一個Parent.cpp文件,*.cpp文件是clang命令編譯Parent.m文件的輸出(相當于我們Xcode的編譯操作)。
  3. 打開Parent.cpp文件,我們看到有2段代碼是我們關注的
......省略的代碼......
struct __rw_objc_super { 
    struct objc_object *object; 
    struct objc_object *superClass; 
    __rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {} 
};
......省略的代碼......

//這個地方就是我們Son類的init方法
static instancetype _I_Son_init(Son * self, SEL _cmd) {
    if (self = ((Son *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("init"))) {


        NSLog((NSString *)&__NSConstantStringImpl__var_folders_fz_rpw560ws5gxgsnqc6l8c6mqh0000gn_T_Parent_12a896_mi_0,((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class")));
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_fz_rpw560ws5gxgsnqc6l8c6mqh0000gn_T_Parent_12a896_mi_1,((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("class")));
    }
    return self;
}
......省略的代碼......

第三步 我們分析一下這塊代碼

先分析一下如下代碼,只看 [self class] 和 [super class] 塊代碼

// [self class] 等價于下面代碼
((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))

// [super class] 等價于下面代碼
((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("class"))

// [self class]  和  [super class] 代碼聲明區(qū)別如下
//[self class]
objc_msgSend(id, SEL)
//[super class]
objc_msgSendSuper(__rw_objc_super *, SEL)

解釋一下區(qū)別:
1. [self class]代碼底層是objc_msgSend(id, SEL),
   [super class]代碼底層是objc_msgSendSuper(__rw_objc_super *, SEL)
2. SEL 是方法選擇器 都是 - (Class)class 方法
3. 它們的方法名字不同 
4. 第一個參數(shù)不同,objc_msgSend的第一個參數(shù)是id, 
    objc_msgSendSuper的第一個參數(shù)是 __rw_objc_super 是一個結構體,如下
struct __rw_objc_super { 
    struct objc_object *object; //當前對象,是self即Son類
    struct objc_object *superClass; //當前對象的父類,即Parent
    __rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {} 
};

// [self class]  和  [super class] 代碼實現(xiàn)區(qū)別如下
//[self class]
objc_msgSend(
             (id)self, //當前類,即Son類
             sel_registerName("class")//從self類中開始查找class方法
            )
//[super class]
objc_msgSendSuper(
                  (__rw_objc_super){
                                    (id)self, //還是這個self,這個和上面objc_msgSend中的self是一樣的,都是Son這個類
                                    (id)class_getSuperclass(objc_getClass("Son"))//從class_getSuperclass(objc_getClass("Son")類(即Person類)中開始查找class方法
                                   }, 
                  sel_registerName("class")//查找的方法
                 )

從上述分析中可以看出
[self class] 和 [super class] 的區(qū)別也就是
objc_msgSend(id, SEL) 和 objc_msgSendSuper(__rw_objc_super *, SEL)的區(qū)別:

a. objc_msgSend查找class方法的起始位置是當前類即Son。
b. objc_msgSendSuper查找class方法的起始位置是class_getSuperclass(objc_getClass("Son"))即Person類,
最終objc_msgSendSuper也會轉(zhuǎn)為objc_msgSend( (id)self, sel_registerName("class"))方式

  • 只是查找調(diào)用class方法的起始位置不同而已,最終調(diào)用方式是一樣的,這個是第一個關鍵點

說的在明白一點就是:
首先你要知道 Son繼承Parent,Parent繼承NSObject類

[self class]調(diào)用Son類中的class方法,找到class方法后調(diào)用即可。
如果沒有找到class方法后,開始向父類中查找即Parent類,如果還沒有找到的話,開始向NSObject類中找,
當然class方法在Son類沒有實現(xiàn),所以此處調(diào)用的是NSObject類中的class方法
class 查找順序是Son -> Parent -> NSObject 中的class方法

[super class]調(diào)用Parent類中的class方法,找到class方法后調(diào)用即可。
如果沒有找到class方法后,開始向父類中查找即NSObject類,
當然class方法在Parent類也沒有實現(xiàn),所以此處調(diào)用的也是NSObject類中的class方法
class 查找順序是 Parent -> NSObject 中的class方法

既然都是在NSObject中找到的方法,那為什么輸出的都是Son呢?
網(wǎng)上的其他文章看了半天還是不明白為什么輸出的都是Son,其他文章只說了上面的一個關鍵點即【只是他們查找方法的起始位置不同,最終調(diào)用方式是一樣的】
最終都是執(zhí)行objc_msgSend( (id)self, sel_registerName("class"))
還有一個關鍵點在NSObject的class的方法的實現(xiàn),如下

-(Class)class { 
  return object_getClass(self); //這個self是Son類的對象
}

[self class][super class] 最終都執(zhí)行
objc_msgSend( (id)self, sel_registerName("class")) 發(fā)送消息
其中self對象都是(即Son類),NSObject中的class方法需要一個參數(shù)self,而此時的self就是Son類,
所以[self class][super class] 輸出的都是Son

總結:

  1. [self method][super method] 的不同點是開始查找method的方法的起始位置不同.
  2. 最終都是執(zhí)行objc_msgSend( (id)self, sel_registerName("class"))這個,只是本例中有一個特殊點即NSObject類的方法class需要一個參數(shù),該參數(shù)就是調(diào)用這個方法的對象(即 self)。
-(Class)class { 
  return object_getClass(self); //這個self是Son類的對象
}

可以寫一個demo 加以驗證,代碼如下:

#import <Foundation/Foundation.h>

@interface Parent : NSObject

- (void)run;

@end

@implementation Parent

- (void)run{
    NSLog(@"Parent: run,當前self是:%@", NSStringFromClass([self class]));
}

@end

@interface Son : Parent
- (void)run;

@end

@implementation Son

- (void)run{
    NSLog(@"Son: run,當前self是:%@", NSStringFromClass([self class]));
    
}

- (instancetype)init{
    if (self = [super init]) {
        [self run];
        [super run];
    }
    return self;
}

@end

最終輸出結果:
2018-02-12 14:56:23.916168 testProject[451:136266] Son: run,當前self是:Son
2018-02-12 14:56:23.916250 testProject[451:136266] Parent: run,當前self是:Son

還是執(zhí)行上面的clang命令,可以看到如下編譯后的源碼

// Parent.m 類的run編譯
static void _I_Parent_run(Parent * self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_fz_rpw560ws5gxgsnqc6l8c6mqh0000gn_T_Parent_e1bd12_mi_0, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))));
}

// Son.m 類的run編譯
static void _I_Son_run(Son * self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_fz_rpw560ws5gxgsnqc6l8c6mqh0000gn_T_Parent_e1bd12_mi_1, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))));

}
// Son.m 類的init編譯
static instancetype _I_Son_init(Son * self, SEL _cmd) {
    if (self = ((Son *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("init"))) {
        ((void (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("run"));
        ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("run"));
    }
    return self;
}

上述代碼驗證了

  1. 最終都會執(zhí)行objc_msgSend((id)self, sel_registerName("run")) 且self都是Son對象
  2. 查找的位置不同,[self run]執(zhí)行了Son類的run方法并輸出內(nèi)容, [super run]執(zhí)行了Parent類的run方法并輸出內(nèi)容
1518422017310.jpg

以上內(nèi)容如果你感覺有問題的話,可以評論交流...

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,032評論 0 9
  • 本文轉(zhuǎn)載自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex閱讀 867評論 0 1
  • 朋友小希畢業(yè)后來到一家大型企業(yè)工作剛剛不到兩年。作為最小的成員,小希把比自己大幾歲的女同事都看作姐姐來相處。由于一...
    泋小溪閱讀 1,188評論 2 4
  • 每一年待到油菜花盛開之時,便是一副春日融融,陽光明媚的景色。非常適合,外出踏青賞景。 無奈各種原因牽絆,實在沒辦法...
    灰色的破旗閱讀 365評論 0 1

友情鏈接更多精彩內(nèi)容