前陣子我學(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
NSObject
的 metaclass
是有地址的州叠,而并不是博主所說的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)系相同了。
有問題歡迎在評(píng)論區(qū)指出哦香府。