深入代碼理解instance拖云、class object领猾、metaclass
面向?qū)ο缶幊讨校钪匾母拍罹褪穷惷看希旅嫖覀兙蛷拇a入手泣矛,看看OC是如何實(shí)現(xiàn)類的疲眷。
instance對象實(shí)例
我們經(jīng)常使用id
來聲明一個對象,那id
的本質(zhì)又是什么呢您朽?打開#import<objc/objc.h>
文件狂丝,可以發(fā)現(xiàn)以下幾行代碼
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
通過注釋和代碼不難發(fā)現(xiàn),我們創(chuàng)建的一個對象或?qū)嵗鋵?shí)就是一個struct objc_object
結(jié)構(gòu)體哗总,而我們常用的id
也就是這個結(jié)構(gòu)體的指針几颜。
這個結(jié)構(gòu)體只有一個成員變量,這是一個Class
類型的變量isa
讯屈,也是一個結(jié)構(gòu)體指針菠剩,那這個指針又指向什么呢?
面向?qū)ο笾忻恳粋€對象都必須依賴一個類來創(chuàng)建耻煤,因此對象的isa
指針就指向?qū)ο笏鶎俚念惛鶕?jù)這個類模板能夠創(chuàng)建出實(shí)例變量、實(shí)例方法等准颓。
比如有如下代碼
NSString *str = @"Hello World";
通過上文我們知道這個str
對象本質(zhì)就是一個objc_object
結(jié)構(gòu)體哈蝇,而這個結(jié)構(gòu)體的成員變量isa
指針則表明了str is a NSString
,因此這個isa
就指向了NSString
類攘已,這個NSString
類其實(shí)是類對象炮赦,不明白就繼續(xù)往下看。
class object(類對象)/metaclass(元類)
繼續(xù)查看結(jié)構(gòu)體objc_class
的定義
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
struct objc_classs
結(jié)構(gòu)體里存放的數(shù)據(jù)稱為元數(shù)據(jù)(metadata)
样勃,通過成員變量的名稱我們可以猜測里面存放有指向父類的指針吠勘、類的名字、版本峡眶、實(shí)例大小剧防、實(shí)例變量列表、方法列表辫樱、緩存峭拘、遵守的協(xié)議列表等,這些信息就足夠創(chuàng)建一個實(shí)例了,該結(jié)構(gòu)體的第一個成員變量也是isa
指針鸡挠,這就說明了Class
本身其實(shí)也是一個對象辉饱,我們稱之為類對象
,類對象
在編譯期產(chǎn)生用于創(chuàng)建實(shí)例對象拣展,是單例彭沼,因此前文中的栗子其實(shí)應(yīng)該表達(dá)為str的isa指針指向了NSString類對象
那么這個結(jié)構(gòu)體的isa
指針又指向什么呢?
類對象
中的元數(shù)據(jù)
存儲的都是如何創(chuàng)建一個實(shí)例的相關(guān)信息备埃,那么類對象
和類方法
應(yīng)該從哪里創(chuàng)建呢姓惑?就是從isa
指針指向的結(jié)構(gòu)體創(chuàng)建,類對象
的isa
指針指向的我們稱之為元類(metaclass)
瓜喇,元類
中保存了創(chuàng)建類對象
以及類方法
所需的所有信息挺益,因此整個結(jié)構(gòu)應(yīng)該如下圖所示:
通過上圖我們可以清晰的看出來一個實(shí)例對象也就是struct objc_object
結(jié)構(gòu)體它的isa
指針指向類對象
,類對象
的isa
指針指向了元類乘寒,super_class
指針指向了父類的類對象
望众,而元類
的super_class
指針指向了父類的元類
,那元類
的isa
指針又指向了什么伞辛?為了更清晰的表達(dá)直接使用一個大神畫的圖烂翰。
通過上圖我們可以看出整個體系構(gòu)成了一個自閉環(huán),如果是從NSObject
中繼承而來的上圖中的Root class
就是NSObject
蚤氏。至此甘耿,整個實(shí)例
、類對象
竿滨、元類
的概念也就講清了佳恬,接下來我們在代碼中看看這些概念該怎么應(yīng)用。
類對象
有如下代碼
@interface Person : NSObject
@property (nonatomic, copy) NSString* name;
@property (nonatomic, assign) NSUInteger age;
@end
@implementation Person
@synthesize name = _name;
@synthesize age = _age;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
Class c1 = [p class];
Class c2 = [Person class];
//輸出 1
NSLog(@"%d", c1 == c2);
}
return 0;
}
c1
是通過一個實(shí)例對象獲取的Class
于游,實(shí)例對象可以獲取到其類對象
毁葱,類名作為消息的接受者時代表的是類對象
,因此類對象獲取Class
得到的是其本身贰剥,同時也印證了類對象
是一個單例的想法倾剿。
那么如果我們想獲取isa
指針的指向?qū)ο竽兀?/p>
介紹兩個函數(shù)
OBJC_EXPORT BOOL class_isMetaClass(Class cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
OBJC_EXPORT Class object_getClass(id obj)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
class_isMetaClass
用于判斷Class
對象是否為元類
,object_getClass
用于獲取對象的isa
指針指向的對象蚌成。
再看如下代碼:
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc] init];
//輸出1
NSLog(@"%d", [p class] == object_getClass(p));
//輸出0
NSLog(@"%d", class_isMetaClass(object_getClass(p)));
//輸出1
NSLog(@"%d", class_isMetaClass(object_getClass([Person class])));
//輸出0
NSLog(@"%d", object_getClass(p) == object_getClass([Person class]));
}
return 0;
}
通過代碼可以看出前痘,一個實(shí)例對象通過class
方法獲取的Class
就是它的isa
指針指向的類對象
,而類對象
不是元類
担忧,類對象
的isa
指針指向的對象是元類
芹缔。
聲明:原文轉(zhuǎn)載自http://blog.csdn.net/u014205968的博文,經(jīng)過自己的修改加工僅作為記錄學(xué)習(xí)使用瓶盛。