导语
今天在StackOverFlow上面看到一个提问:object_getClass(obj) and [obj class] give different results。刚好自己也有类似的困惑,于是想稍微研究一下。
在Object-C的类型结构中,有几个比较重要的概念:Object(实例),Class(类),Metaclass(元类),Rootclass(根类),Rootclass‘s metaclass(根元类),且这些都是对象。因此标题中的“obj”这个对象可以是上面五个概念的任意一个,需要一一探讨。
1.obj为Object实例对象
在Object-C中,Object本质上是一个struct,在这个struct中会保存一个名为isa的指针,该指针会指向该Object的类。定义如下所示:
typedef struct objc_object {
Class isa;
} *id;
下面是代码测试:
//obj为实例变量
id obj = [TestObject new];
Class cls = object_getClass(obj);
Class cls2 = [obj class];
NSLog(@"%p" , cls);
NSLog(@"%p" , cls2);
输出结果:
2015-12-17 14:08:32.196 TestProject[658:29503] 0x100001140
2015-12-17 14:08:32.197 TestProject[658:29503] 0x100001140
结论:当obj为实例变量时,object_getClass(obj)与[obj class]输出结果一直,均获得isa指针,即指向类对象的指针。
2.obj为Class类对象
在Objective-C中,任何类的定义都是对象。类和类的实例(对象)没有任何本质上的区别。任何对象都有isa指针。
typedef struct objc_class *Class;
struct objc_class {
Class isa;
Class super_class;
/* followed by runtime specific details... */
};
下面是代码测试:
//obj为实例变量
id obj = [TestObject new];
//classObj为类对象
Class classObj = [obj class];
Class cls = object_getClass(classObj);
Class cls2 = [classObj class];
NSLog(@"%p" , cls);
NSLog(@"%p" , cls2);
输出结果:
2015-12-17 14:25:48.785 TestProject[813:38336] 0x100001118
2015-12-17 14:25:48.786 TestProject[813:38336] 0x100001140
结论:当obj为类对象时,object_getClass(obj)返回类对象中的isa指针,即指向元类对象的指针;[obj class]返回的则是其本身。
3.obj为Metaclass类对象
Metaclass与Class的结构是一样的,只是职能不同。Class结构中是存储Object实例的相关数据,而Metaclass则是存储Class相关的数据。
下面的测试代码:
//obj为实例变量
id obj = [TestObject new];
//classObj为类对象
Class classObj = [obj class];
//metaClassObj为元类对象
Class metaClassObj = object_getClass(classObj);
Class cls = object_getClass(metaClassObj);
Class cls2 = [metaClassObj class];
NSLog(@"%p" , cls);
NSLog(@"%p" , cls2);
输出结果:
2015-12-17 14:41:23.386 TestProject[921:47694] 0x7fff78fb9118
2015-12-17 14:41:23.387 TestProject[921:47694] 0x100001118
结论:当obj为Metaclass(元类)对象时,object_getClass(obj)返回元类对象中的isa指针,因为元类对象的isa指针指向根类,所有返回的是根类对象的地址指针;[obj class]返回的则是其本身。
4.obj为Rootclass类对象
Rootclass顾名思义就是根类,任何类的Metaclass中的isa指针都是指向根类。且结构与Class结构是一样的。
测试代码:
//obj为实例变量
id obj = [TestObject new];
//classObj为类对象
Class classObj = [obj class];
//metaClassObj为元类对象
Class metaClassObj = object_getClass(classObj);
//rootClassObj为元类对象
Class rootClassObj = object_getClass(metaClassObj);
Class cls = object_getClass(rootClassObj);
Class cls2 = [rootClassObj class];
NSLog(@"%p" , cls);
NSLog(@"%p" , cls2);
输出结果:
2015-12-17 14:52:34.633 TestProject[965:54693] 0x7fff78fb9118
2015-12-17 14:52:34.634 TestProject[965:54693] 0x7fff78fb9118
结论:当obj为Rootclass(元类)对象时,object_getClass(obj)返回根类对象中的isa指针,因为跟类对象的isa指针指向Rootclass‘s metaclass(根元类),即返回的是根元类的地址指针;[obj class]返回的则是其本身。
因为根类的isa指针其实是指向本身的,所有根元类其实就是根类,所有输出的结果是一样的。
总结:经上面初步的探索得知,object_getClass(obj)返回的是obj中的isa指针;而[obj class]则分两种情况:一是当obj为实例对象时,[obj class]中class是实例方法:- (Class)class,返回的obj对象中的isa指针;二是当obj为类对象(包括元类和根类以及根元类)时,调用的是类方法:+ (Class)class,返回的结果为其本身。
最后,奉上一张经典的Object-C的对象模型图: