Objective-C中其實(shí)有三種對(duì)象,分別為實(shí)例對(duì)象(instance)、類對(duì)象(class)、元類對(duì)象(meta-class);那這三種對(duì)象有什么關(guān)系呢?以及三種對(duì)象本身存儲(chǔ)了哪些信息呢?這是我們本文要探討的問(wèn)題。
我們知道Objective-C的類可以通過(guò)clang編譯器轉(zhuǎn)換為C++的結(jié)構(gòu)體,也就是說(shuō),Objective-C對(duì)象的本質(zhì)就是結(jié)構(gòu)體,下面通過(guò)實(shí)踐來(lái)證明。
- 創(chuàng)建一個(gè)命令行項(xiàng)目(這里可以找到),在main.m文件添加如下的代碼
#import <Foundation/Foundation.h>
@interface Person: NSObject {
int _age;
double _height;
}
@end
@implementation Person
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
}
return 0;
}
然后使用clang編譯器重寫main.m文件,可指定平臺(tái)iOS和架構(gòu)arm64,在main.m所在的文件夾執(zhí)行如下命令
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
會(huì)得到一個(gè)main-arm64.cpp的文件,可看到Person類轉(zhuǎn)換為struct Person_IMPL結(jié)構(gòu)體
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
double _height;
};
存在上面定義Person類時(shí)聲明的_age和_height成員變量,發(fā)現(xiàn)第一個(gè)成員變量是struct NSObject_IMPL結(jié)構(gòu)體,這是NSObject被clang編譯器重寫后對(duì)應(yīng)的結(jié)構(gòu)體,在main-arm64.cpp文件中可找到聲明
struct NSObject_IMPL {
Class isa;
};
實(shí)例對(duì)象(instance)
Objictive-C實(shí)例對(duì)象是通過(guò)類對(duì)象創(chuàng)建
Person *p1 = [Person new];
Person *p2 = [[Person alloc] init];
實(shí)例對(duì)象分配在堆空間,所以p1、p2指向的內(nèi)存地址都在堆上;每次創(chuàng)建的都是不同的對(duì)象,所以在內(nèi)存中會(huì)存在很多個(gè)實(shí)例對(duì)象。
把struct NSObject_IMPL與struct Person_IMPL合并一下得到下面的結(jié)構(gòu)
struct Person_IMPL {
Class isa;
int _age;
double _height;
};
這也是Person實(shí)例對(duì)象在內(nèi)存中的布局情況。
總結(jié)一下實(shí)例對(duì)象中包含信息
- isa指針,指向類對(duì)象
- 成員變量
其實(shí)isa也是成員變量,只不過(guò)比較特殊,它指向這個(gè)實(shí)例對(duì)象的類對(duì)象。
類對(duì)象(class)
類對(duì)象是對(duì)實(shí)例對(duì)象的描述,描述這個(gè)類實(shí)例對(duì)象有哪些成員變量、屬性、對(duì)象方法、類方法,那這些信息存儲(chǔ)在什么地方呢?(下面提及的數(shù)據(jù)類型這里可以找到)
類對(duì)象聲明為typedef struct objc_class *Class;,再看看struct objc_class結(jié)構(gòu)體,截取一些關(guān)鍵的信息看看
struct objc_class : objc_object {
// Class ISA;
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 *data() {
return bits.data();
}
// ... 還有很多方法,略去
};
superclass指向父類,其他的信息存儲(chǔ)在class_rw_t的結(jié)構(gòu)體中,定義如下
struct class_rw_t {
const class_ro_t *ro; // 實(shí)例變量信息
method_array_t methods; // 對(duì)象方法信息
property_array_t properties; // 屬性信息
protocol_array_t protocols; // 協(xié)議信息
// ... 省略其他信息
}
runtime提供這些信息函數(shù)如下
獲取實(shí)例變量
Ivar *
class_copyIvarList(Class cls, unsigned int *outCount)
獲取屬性信息
objc_property_t *
class_copyPropertyList(Class cls, unsigned int *outCount)
獲取對(duì)象方法
// 第一個(gè)參數(shù)是 *類對(duì)象*,返回的是*對(duì)象方法*;如果是參數(shù)*元類對(duì)象*,返回的是*對(duì)象方法*
Method *
class_copyMethodList(Class cls, unsigned int *outCount)
獲取協(xié)議信息
Protocol * __unsafe_unretained *
class_copyProtocolList(Class cls, unsigned int *outCount)
struct objc_class繼承objc_object,struct objc_object結(jié)構(gòu)如下
struct objc_object {
private:
isa_t isa;
// ...省略方法
}
那isa_t是聯(lián)合體,結(jié)構(gòu)如下
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
// ...省略
}
這里面有個(gè) cls成員,這個(gè)就是class指向mata-class的指針。
總結(jié)一下類對(duì)象(class)中存儲(chǔ)的信息
- isa指針,指向元類的指針
- superclass指向父類的指針
- 成員變量(Ivar)的描述信息
- 屬性(property)的描述信息
- 對(duì)象方法(Instance Method)的描述信息
- 遵循協(xié)議(protocol)的描述信息
元類對(duì)象(meta-class)
上面可以看出,meta-class也是一個(gè)Class 類型,與類對(duì)象的數(shù)據(jù)類型時(shí)一樣的,只不過(guò)存儲(chǔ)的信息不同。
元類對(duì)象(meta-class)存儲(chǔ)的信息有
- isa指針,指向基類(NSObject)元類(meta-class)的指針,所有元類(meta-class)的isa指針都指向NSObject元類的指針。
- superclass指向父類的元類(meta-class)的指針
- 類方法(class Method)的描述信息
總結(jié)
上面講了實(shí)例對(duì)象的內(nèi)存布局,以及實(shí)例對(duì)象(instance)、類對(duì)象(class)、元類對(duì)象(meta-class)之間的關(guān)系,最后用一張圖來(lái)說(shuō)明它們之間的關(guān)系。
