講述 objective-c 2.0 對(duì) class 的定義偏化,類脐恩、元類的關(guān)系,一些面試題侦讨,并且對(duì) block 簡(jiǎn)單說(shuō)明驶冒。
網(wǎng)上很多文章對(duì) class 的定義還是 OC 1.0 的,因此自己寫(xiě)一篇記錄一下韵卤。
iOS 運(yùn)行時(shí)代碼 objc4 的下載地址:
https://opensource.apple.com/tarballs/objc4/
打開(kāi)源代碼骗污,Xcode 同時(shí)按 cmd + shift + o,輸入 Class 可以搜到相關(guān)定義沈条。
1需忿、簡(jiǎn)化定義
// Class
typedef struct objc_class *Class;
// id
typedef struct objc_object *id;
// 類
struct objc_class : objc_object {
// 省略部分成員變量以及方法...
}
// 對(duì)象
struct objc_object {
// 省略部分成員變量以及方法...
}
// Object
@interface Object {
Class isa;
}
@end
// NSObject
@interface NSObject <NSObject> {
Class isa;
}
可見(jiàn),類和對(duì)象是結(jié)構(gòu)體。
類也是對(duì)象屋厘,因?yàn)閷?duì)象和類都是結(jié)構(gòu)體 objc_object涕烧。
2、詳細(xì)定義
2.1 對(duì)象
結(jié)構(gòu)體 objc_object:
struct objc_object {
private:
isa_t isa;
// 省略部分成員變量以及方法...
}
聯(lián)合體 isa_t:
union isa_t {
Class cls;
uintptr_t bits;
// 省略部分成員變量以及方法...
};
對(duì)象有個(gè) isa_t 汗洒,isa_t 有個(gè) Class 指向?qū)ο蟮念悺?br>
類也是對(duì)象议纯,類的 isa_t 的 Class 指向元類。
元類保存類方法仲翎,類保存實(shí)例方法痹扇。
2.2 類
結(jié)構(gòu)體 objc_class:
struct objc_class : objc_object {
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
// class_rw_t 有類的方法和屬性列表。
class_rw_t *data() {
return bits.data();
}
// 省略部分成員變量以及方法...
}
superclass 是父類溯香,
cache_t 是方法緩存鲫构,
class_data_bits_t 包含 class_rw_t,
class_rw_t 里面有類的方法和屬性列表玫坛。
結(jié)構(gòu)體 class_rw_t:
struct class_rw_t {
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
const class_ro_t *ro;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
// 省略部分成員變量以及方法...
};
method_array_t 是個(gè)類结笨,保存方法列表,
property_array_t 保存屬性列表湿镀,
protocol_array_t 保存協(xié)議列表炕吸。
objc_object 可以簡(jiǎn)單理解為,有個(gè) isa 指向?qū)ο蟮念悾?br>
objc_class 比 objc_object 多了方法列表勉痴、屬性列表赫模。
然后類、元類也是對(duì)象蒸矛。
3瀑罗、對(duì)象、類雏掠、元類的關(guān)系
類斩祭、元類,也是對(duì)象乡话,他們的關(guān)系如下圖所示:
Root class 是 NSObject绑青。
- 對(duì)于 instance诬像,沒(méi)有 superclass 線,isa 都指向自己的類闸婴。
- 對(duì)于 class颅停,superclass 指向父類,isa 指向元類掠拳。
- 對(duì)于 meta癞揉,superclass 指向父類,isa 指向同一個(gè)元類,即 NSObject meta class喊熟。
- NSObject 沒(méi)有父類柏肪,所以 superclass 指向 nil。
- NSObject meta class 的 isa 指向 NSObject 類芥牌,形成閉環(huán)烦味。
為什么所有元類的 isa 都指向 NSObject meta class?
為什么 NSObject meta class 的 superclass 要指向 NSObject壁拉?
有沒(méi)有大神解答一下??谬俄。
查找對(duì)象方法時(shí),根據(jù)對(duì)象的 isa 找到對(duì)象的類弃理,如果子類沒(méi)有找到方法溃论,就通過(guò) superclass 找到父類,在父類查找方法痘昌。
查找類方法時(shí)钥勋,根據(jù)類的 isa 找到元類,如果子元類沒(méi)有找到辆苔,就通過(guò) superclass 找到父元類算灸,在父元類查找方法。
如果根元類驻啤,即 NSObject meta class 也沒(méi)找到菲驴,就會(huì)去 superclass,也就是 NSObject 類查找骑冗。
如果想深入了解赊瞬,可以看這篇文章:《神經(jīng)病院Objective-C Runtime入院第一天——isa和Class》。
4沐旨、一些題目
4.1
類調(diào)用實(shí)例方法森逮,是否會(huì)崩潰榨婆?
// 分類
@interface NSObject (Test)
+ (void)ioo;
@end
@implementation NSObject (Test)
- (void)ioo {
NSLog(@"%@", NSStringFromSelector(_cmd));
}
@end
測(cè)試用例:
- (void)test1 {
[NSObject ioo]; // 1磁携、
[NSString ioo]; // 2、
}
1 和 2良风,哪個(gè)會(huì)崩潰谊迄?
答案是都不會(huì)崩潰。
對(duì)于 [NSObject ioo]
烟央,先在元類 NSObject meta class 里面找不到 ioo 方法统诺,然后在元類的父類 NSObject class 里找到了。元類 NSObject meta class 的 superclass 指向類 NSObject class 疑俭。
對(duì)于 [NSString ioo]
粮呢,先在 NSString meta class 里面找不到,然后在 superclass 指向的 NSObject meta class 里也找不到,最后在 NSObject meta class 的 superclass 指向的 NSObject class 里找到了啄寡。
4.2
下面的代碼是否會(huì)崩潰
// 分類
@interface NSString (Test)
+ (void)sioo;
@end
@implementation NSString (Test)
- (void)sioo {
NSLog(@"%@", NSStringFromSelector(_cmd));
}
@end
- (void)test2 {
[NSString ioo]; // 1豪硅、
[NSString sioo]; // 2、
}
1 不會(huì)崩潰挺物,2 會(huì)崩潰懒浮。
對(duì)于 [NSString sioo]
,尋找的過(guò)程是 NSString meta class识藤、NSObject meta class砚著、NSObject,而方法 sioo 是定義在 NSString 里面痴昧,所以崩潰了稽穆。
另外,假設(shè)只聲明但沒(méi)有實(shí)現(xiàn)方法 - (void)fun
剪个,
對(duì)于 [[NSObject new] fun]
秧骑,在 NSObject 里找不到,而 NSObject 的父類是 nil扣囊,因此崩潰了乎折。
4.3
下面代碼輸出什么
- (void)test3 {
BOOL b1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL b2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL b3 = [(id)[Son class] isKindOfClass:[Son class]];
BOOL b4 = [(id)[Son class] isMemberOfClass:[Son class]];
NSLog(@"%d %d %d %d", b1, b2, b3, b4);
BOOL b5 = [[Son new] isKindOfClass:[Son class]];
BOOL b6 = [[Son new] isMemberOfClass:[Son class]];
NSLog(@"%d %d", b5, b6);
BOOL b7 = class_isMetaClass([Son class]); // 要 #import <objc/runtime.h>
BOOL b8 = [Son new].class == [Son class];
BOOL b9 = class_isMetaClass([[Son class] class]);
NSLog(@"%d %d %d", b7, b8, b9);
}
b1-4 輸出 1 0 0 0,
b5-6 輸出 1 1侵歇,
b7-9 輸出 0 1 0骂澄。
先看 class 方法:
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
方法 object_getClass 是獲取對(duì)象的 isa 指向的 class。
類和對(duì)象的 class 方法都返回類惕虑,不是元類坟冲。
類的 class 方法返回 self,對(duì)象的返回所屬類溃蔫。
舉個(gè)栗子:
po [Son class]
po [[[[Son class] class] class] class]
上面都是輸出 Son健提,不管調(diào)用多少次 class,返回的都是 self
然后看下 isMemberOfClass 方法:
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
方法 object_getClass 是獲取對(duì)象的 isa 指向的 class伟叛,如果傳入的參數(shù)是類對(duì)象私痹,獲取的就是元類了。
方法 isMemberOfClass 會(huì)取一次 isa 指向的類统刮,然后進(jìn)行比較紊遵。
如果是對(duì)象調(diào)用 isMemberOfClass,就是類與類比較侥蒙。
如果是類調(diào)用 isMemberOfClass暗膜,就是元類與類比較。
舉個(gè)栗子:
[(id)[A class] isMemberOfClass:[B class]];
上面代碼是 A meta class鞭衩,與 B class 進(jìn)行比較学搜,
而不是 A class 與 B class 比較娃善。
最后看下 isKindOfClass :
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
可見(jiàn) isKindOfClass 就是 isMemberOfClass 的循環(huán)版,會(huì)通過(guò) superclass 沿著繼承鏈進(jìn)行判斷瑞佩。
如果是對(duì)象調(diào)用 isMemberOfClass会放,會(huì)沿著 class 繼承鏈判斷。
如果是類調(diào)用 isMemberOfClass钉凌,會(huì)沿著 meta class 繼承鏈判斷咧最。
需要注意的是,NSObject meta class 的 superclass 是 NSObject class御雕,isa 指向自己矢沿。
NSObject class 的 superclass 是 nil,isa 指向 NSObject meta class酸纲。
對(duì)于
BOOL b1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]
捣鲸,
先是 NSObject meta class 與 NSObject class 比較,
然后是 NSObject class 與 NSObject class 比較闽坡,所以結(jié)果是 1栽惶。
總結(jié)一下:
- (void)test3 {
// 類和對(duì)象的 class 方法會(huì)返回類,不是元類疾嗅。
// 類的 class 方法返回 self外厂,對(duì)象的返回所屬類。
// isMemberOfClass 會(huì)取 isa 指向的類代承,與參數(shù)的類就行比較
// 如果是對(duì)象調(diào)用 isMemberOfClass汁蝶,就是類與類比較
// 如果是類調(diào)用 isMemberOfClass,就是元類與類比較
// isKindOfClass 會(huì)通過(guò) superclass 沿著繼承鏈循環(huán)判斷
BOOL b1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; // 1论悴,循環(huán)到 NSObject == NSObject
BOOL b2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; // 0掖棉,NSObject meta != NSObject
BOOL b3 = [(id)[Son class] isKindOfClass:[Son class]]; // 0,從 Son meta class 循環(huán)到 NSObject 到 nil膀估,都 != Son
BOOL b4 = [(id)[Son class] isMemberOfClass:[Son class]]; // 0幔亥,Son meta != Son
b3 = [Son isKindOfClass:[Son class]]; // 0,和上面的兩句是一樣的
b4 = [Son isMemberOfClass:[Son class]]; // 0察纯,Son meta != Son
NSLog(@"%d %d %d %d", b1, b2, b3, b4);// 1 0 0 0
BOOL b5 = [[Son new] isKindOfClass:[Son class]]; // 1
BOOL b6 = [[Son new] isMemberOfClass:[Son class]]; // 1帕棉,Son == Son
NSLog(@"%d %d", b5, b6); // 1 1
BOOL b7 = class_isMetaClass([Son class]); // 0,要 #import <objc/runtime.h>
BOOL b8 = [Son new].class == [Son class]; // 1捐寥,Son == Son
BOOL b9 = class_isMetaClass([[Son class] class]); // 0笤昨,[[Son class] class] == Son
NSLog(@"%d %d %d", b7, b8, b9);// 0 1 0
}
5祖驱、Block
最后簡(jiǎn)單說(shuō)一下 block握恳。block 也是對(duì)象。
定義一個(gè)繼承自 NSObject 的 Test 類捺僻,有個(gè) test 方法:
@implementation Test
- (void)test {
void (^blc)(void) = ^ {
printf("哈哈哈");
};
blc();
}
@end
代碼轉(zhuǎn)換后乡洼, block 語(yǔ)法轉(zhuǎn)換的函數(shù):
// block 的函數(shù)崇裁,參數(shù)是一個(gè) block 指針
static void __Test__test_block_func_0(struct __Test__test_block_impl_0 *__cself) {
printf("哈哈哈");
}
block 轉(zhuǎn)換成結(jié)構(gòu)體:
// 自定義 block 的結(jié)構(gòu)體
struct __Test__test_block_impl_0 {
struct __block_impl impl; // block 的基本定義
struct __Test__test_block_desc_0* Desc; // block 的數(shù)據(jù)
// 構(gòu)造函數(shù)。
// fp 是 block 的函數(shù)的指針束昵,desc 是 block 的數(shù)據(jù)拔稳。
__Test__test_block_impl_0(void *fp, struct __Test__test_block_desc_0 *desc, int flags=0) {
// 省略部分代碼
}
};
再看 block 的基本定義:
// block 的基本定義
struct __block_impl {
void *isa; // block 的類
int Flags;
int Reserved;
void *FuncPtr; // 指向 block 的函數(shù)
};
里面有個(gè) isa 指針,指向 block 的類锹雏,可以是堆巴比、棧、全局 block礁遵。
打印輸出 block 的繼承鏈:
(lldb) po [blc class]
__NSGlobalBlock__
(lldb) po [blc superclass]
__NSGlobalBlock
(lldb) po [[blc superclass] superclass]
NSBlock
(lldb) po [[[blc superclass] superclass] superclass]
NSObject
(lldb) po [[[[blc superclass] superclass] superclass] superclass]
nil
具體可以看我的另一篇文章 筆記-《Objective-C高級(jí)編程 iOS與OS X多線程和內(nèi)存管理》轻绞,在 2.3 章節(jié)記錄 Blocks 的實(shí)現(xiàn)。
如有錯(cuò)誤佣耐,歡迎指正政勃。