Runtime 中關(guān)于MetaClass的問題

前陣子我學(xué)習(xí)了Objective-C中的Runtime庫缸夹,最近我一個(gè)同學(xué)也在學(xué)習(xí)Runtime铆遭,和我看的是同一篇博客(博客地址)锐极,他學(xué)習(xí)過程時(shí)遇到了一個(gè)問題膘螟,就過來問我成福。

問題是博客中關(guān)于metaclass 的一段代碼

代碼如下:

void TestMetaClass(id self, SEL _cmd) {
    NSLog(@"This objcet is %p", self);
    NSLog(@"Class is %@, super class is %@", [self class], [self superclass]);
    Class currentClass = [self class];
    for (int i = 0; i < 4; i++) {
        NSLog(@"Following the isa pointer %d times gives %p", i, currentClass);
        currentClass = objc_getClass((__bridge void *)currentClass);
    }
    NSLog(@"NSObject's class is %p", [NSObject class]);
    NSLog(@"NSObject's meta class is %p", objc_getClass((__bridge void *)[NSObject class]));
}
#pragma mark -
@implementation Test
- (void)ex_registerClassPair {
    Class newClass = objc_allocateClassPair([NSError class], "TestClass", 0);
    class_addMethod(newClass, @selector(testMetaClass), (IMP)TestMetaClass, "v@:");
    objc_registerClassPair(newClass);
    id instance = [[newClass alloc] initWithDomain:@"some domain" code:0 userInfo:nil];
    [instance performSelector:@selector(testMetaClass)];
}
@end

這段代碼的功能就是通過運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建了一個(gè)類叫TestClass繼承自系統(tǒng)的NSError類,然后給這個(gè)類添加一個(gè)方法實(shí)現(xiàn)是TestMetaClass在這個(gè)方法中來測(cè)試metaclass的相關(guān)內(nèi)容荆残。

運(yùn)行結(jié)果是

2014-10-20 22:57:07.352 mountain[1303:41490] This objcet is 0x7a6e22b0
2014-10-20 22:57:07.353 mountain[1303:41490] Class is TestStringClass, super class is NSError
2014-10-20 22:57:07.353 mountain[1303:41490] Following the isa pointer 0 times gives 0x7a6e21b0
2014-10-20 22:57:07.353 mountain[1303:41490] Following the isa pointer 1 times gives 0x0
2014-10-20 22:57:07.353 mountain[1303:41490] Following the isa pointer 2 times gives 0x0
2014-10-20 22:57:07.353 mountain[1303:41490] Following the isa pointer 3 times gives 0x0
2014-10-20 22:57:07.353 mountain[1303:41490] NSObject's class is 0xe10000
2014-10-20 22:57:07.354 mountain[1303:41490] NSObject's meta class is 0x0

在博客中博主寫到

我們?cè)趂or循環(huán)中奴艾,我們通過objc_getClass來獲取對(duì)象的isa,并將其打印出來脊阴,依此一直回溯到NSObject的meta-class握侧。分析打印結(jié)果,可以看到最后指針指向的地址是0x0嘿期,即NSObject的meta-class的類地址。

我同學(xué)就覺得這段代碼理解起來有問題埋合,我當(dāng)時(shí)學(xué)習(xí)的時(shí)候也敲了這段代碼备徐,但是運(yùn)行結(jié)果和博主一樣就沒怎么在意,我現(xiàn)在回過頭來看這段代碼是覺得有點(diǎn)問題甚颂。

首先蜜猾,我覺得objc_getClass((__bridge void *)currentClass)這段代碼的寫法有問題,在objc/runtime.h中這個(gè)方法的定義是:

OBJC_EXPORT Class _Nullable
objc_getClass(const char * _Nonnull name)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

從中我們可以看見這個(gè)方法傳入的參數(shù)是一個(gè)const char *類型振诬,而博主的用法是傳入了(__bridge void *)currentClass把currentClass轉(zhuǎn)化成void *的類型傳入了方法蹭睡,雖然這樣子代碼是能運(yùn)行的里逆,但是我認(rèn)為用法是錯(cuò)誤的懂鸵。

然后砰粹,我認(rèn)為objc_getClass()這個(gè)方法時(shí)不能得到一個(gè)類的isa指針的照雁,這個(gè)方法的作用只是根據(jù)一個(gè)char *類型的字符數(shù)組來獲取到一個(gè)類就缆,而在objc/runtime.h中給出了另一個(gè)方法

OBJC_EXPORT Class _Nullable
objc_getMetaClass(const char * _Nonnull name)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

這個(gè)方法才能得到metaclass,于是我嘗試了下面的代碼

    NSLog(@"NSObject's class is %p", [NSObject class]);
    NSLog(@"NSObject's meta class is %p", objc_getMetaClass("NSObject"));

發(fā)現(xiàn)輸出為:

2017-11-28 12:49:19.611849+0800 meta-class[4153:64693388] NSObject's class is 0x7fffc271d140
2017-11-28 12:49:19.611855+0800 meta-class[4153:64693388] NSObject's meta class is 0x7fffc271d0f0

NSObjectmetaclass是有地址的州叠,而并不是博主所說的NSObject的metaclass類地址是0x0后众,博主代碼運(yùn)行時(shí)得到的地址為0x0只是方法傳入的參數(shù)不對(duì),返回值是nil祟昭,所以地址是0x0缕坎。

所以我認(rèn)為,所有類和它的meta-class都是有地址的(包括NSObject)篡悟,不然運(yùn)行時(shí)如果metaclass沒地址谜叹,我們調(diào)用類方法是去哪里找到的方法。

于是我將代碼改成如下

void TestMetaClass(id self, SEL _cmd) {
    NSLog(@"this object is %p", self);
    NSLog(@"class is %p, super class is %p, meta-class is %p",[self class],[self superclass], objc_getMetaClass("TestClass"));

    Class currentClass = [self class];

    for (int i = 0; i < 4; i++) {
        NSLog(@"Following the isa pointer %d times gives %p name is %s  is meta-class %d",i,[currentClass  class],class_getName(currentClass),class_isMetaClass(currentClass));
        currentClass = objc_getMetaClass(class_getName(currentClass));
    }
   
    NSLog(@"NSObject's class is %p", [NSObject class]);
    NSLog(@"NSObject's meta class is %p", objc_getMetaClass("NSObject"));
}

@implementation Test

- (void)ex_registerClassPair {
    
    Class newClass = objc_allocateClassPair([NSError class], "TestClass", 0);
    class_addMethod(newClass, @selector(testMetaClass), (IMP)TestMetaClass, "v@:");
    objc_registerClassPair(newClass);
    
    id instance = [[newClass alloc] initWithDomain:@"some domain" code:0 userInfo:nil];
    [instance performSelector:@selector(testMetaClass) ];
}

輸出為

2017-11-28 13:36:07.804037+0800 meta-class[5651:64868327] this object is 0x100408f20
2017-11-28 13:36:07.804172+0800 meta-class[5651:64868327] class is 0x100408d50, super class is 0x7fffbf5bf798, meta-class is 0x100408d80
2017-11-28 13:36:07.804185+0800 meta-class[5651:64868327] Following the isa pointer 0 times gives 0x100408d50 name is TestClass  is meta-class 0
2017-11-28 13:36:07.804194+0800 meta-class[5651:64868327] Following the isa pointer 1 times gives 0x100408d80 name is TestClass  is meta-class 1
2017-11-28 13:36:07.804200+0800 meta-class[5651:64868327] Following the isa pointer 2 times gives 0x100408d80 name is TestClass  is meta-class 1
2017-11-28 13:36:07.804206+0800 meta-class[5651:64868327] Following the isa pointer 3 times gives 0x100408d80 name is TestClass  is meta-class 1
2017-11-28 13:36:07.804212+0800 meta-class[5651:64868327] NSObject's class is 0x7fffc271d140
2017-11-28 13:36:07.804217+0800 meta-class[5651:64868327] NSObject's meta class is 0x7fffc271d0f0

可以看到TestClass 和NSObject 的metaclass都是有地址的搬葬。
但是currentClass = objc_getMetaClass(class_getName(currentClass));這樣寫似乎只能獲取到TestClass的metaclass而并不能再去獲取到metaclass的metaclass畢竟這個(gè)方法的返回值的metaclass而不是isa指向的class叉谜。

綜上所述,一個(gè)類和它的metaclass都是有地址的(包括NSObject)踩萎。

更新


經(jīng)評(píng)論區(qū)的小伙伴SamZL指出

可以通過object_getClass()方法得到類的isa指針停局。

這樣我們的代碼就修改為

void TestMetaClass(id self, SEL _cmd) {
    NSLog(@"this object is %p", self);
    NSLog(@"class is %p, super class is %p, meta-class is %p",[self class],[self superclass], objc_getMetaClass("TestClass"));

    Class currentClass = [self class];

    for (int i = 0; i < 4; i++) {
        NSLog(@"Following the isa pointer %d times gives %p name is %s  is %@ meta-class ",i,[currentClass  class],class_getName(currentClass),(class_isMetaClass(currentClass) ? @"" : @"not"));

        currentClass = object_getClass(currentClass);

    }
   
    NSLog(@"NSObject's class is %p", [NSObject class]);
    NSLog(@"NSObject's meta class is %p", objc_getMetaClass("NSObject"));
}

得到的輸出為

2017-12-12 11:34:21.306288+0800 meta-class[36868:90457953] this object is 0x100502c00
2017-12-12 11:34:21.306428+0800 meta-class[36868:90457953] class is 0x100502a10, super class is 0x7fffbf5bf798, meta-class is 0x100502a40
2017-12-12 11:34:21.306450+0800 meta-class[36868:90457953] Following the isa pointer 0 times gives 0x100502a10 name is TestClass  is not meta-class
2017-12-12 11:34:21.306460+0800 meta-class[36868:90457953] Following the isa pointer 1 times gives 0x100502a40 name is TestClass  is  meta-class
2017-12-12 11:34:21.306467+0800 meta-class[36868:90457953] Following the isa pointer 2 times gives 0x7fffc271d0f0 name is NSObject  is  meta-class
2017-12-12 11:34:21.306474+0800 meta-class[36868:90457953] Following the isa pointer 3 times gives 0x7fffc271d0f0 name is NSObject  is  meta-class
2017-12-12 11:34:21.306480+0800 meta-class[36868:90457953] NSObject's class is 0x7fffc271d140
2017-12-12 11:34:21.306486+0800 meta-class[36868:90457953] NSObject's meta class is 0x7fffc271d0f0
Program ended with exit code: 0

這樣就和我們所理解的isa指針的指向關(guān)系相同了。

關(guān)系如下圖
449095-3e972ec16703c54d.png

有問題歡迎在評(píng)論區(qū)指出哦香府。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末董栽,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子企孩,更是在濱河造成了極大的恐慌锭碳,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勿璃,死亡現(xiàn)場(chǎng)離奇詭異擒抛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)补疑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門歧沪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人莲组,你說我怎么就攤上這事诊胞。” “怎么了锹杈?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵撵孤,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我竭望,道長(zhǎng)邪码,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任咬清,我火速辦了婚禮闭专,結(jié)果婚禮上奴潘,老公的妹妹穿的比我還像新娘。我一直安慰自己喻圃,他們只是感情好萤彩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著斧拍,像睡著了一般雀扶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肆汹,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天愚墓,我揣著相機(jī)與錄音,去河邊找鬼昂勉。 笑死浪册,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的岗照。 我是一名探鬼主播村象,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼攒至!你這毒婦竟也來了厚者?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤迫吐,失蹤者是張志新(化名)和其女友劉穎库菲,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體志膀,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡熙宇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了溉浙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烫止。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖放航,靈堂內(nèi)的尸體忽然破棺而出烈拒,到底是詐尸還是另有隱情,我是刑警寧澤广鳍,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站吓妆,受9級(jí)特大地震影響赊时,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜行拢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一祖秒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦竭缝、人聲如沸房维。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咙俩。三九已至,卻和暖如春湿故,著一層夾襖步出監(jiān)牢的瞬間阿趁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國打工坛猪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脖阵,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓墅茉,卻偏偏與公主長(zhǎng)得像命黔,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子就斤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉悍募,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,715評(píng)論 0 9
  • 原文出處:南峰子的技術(shù)博客 Objective-C語言是一門動(dòng)態(tài)語言,它將很多靜態(tài)語言在編譯和鏈接時(shí)期做的事放到了...
    _燴面_閱讀 1,229評(píng)論 1 5
  • 參考文章 清晰理解Objective-C元類object_getClass(obj)與[obj class]的區(qū)別...
    黑化肥發(fā)灰閱讀 1,033評(píng)論 0 51
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 733評(píng)論 0 2
  • 我們常常會(huì)聽說 Objective-C 是一門動(dòng)態(tài)語言战转,那么這個(gè)「動(dòng)態(tài)」表現(xiàn)在哪呢搜立?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,193評(píng)論 0 7