对象的分类
Object- C
中所有对象可以分为3类,实例对象,类对象,元类对象。其中我们开发者常用的继承自NSObject
都属于实例对象,实例对象通过isa
指针指向的是类对象。类对象通过isa
指向的是元类对象。类对象和元类对象拥有同样的结构,都是来自objc_class
。
objc_class结构
在objc4源码中查找objc_class
可以找到其结构
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
}
其中isa指针式其继承自objc_object
的。本文主要讲解类结构中的isa
、superclass
、bits
,cache
的内容单放一篇。
isa
在之前的内容里面已经讲过,主要是指向类对象或者元类对象的。
superclass
是体现出继承关系的。
cache
是方法缓存。
bits
是类的其他信息,例如成员变量,方法列表,协议,属性。
注意:元类对象结构也是如此,但元类对象里面没有成员变量,协议,属性这些内容。
isa指向图
相信做iOS开发都必须掌握的一个图。
这里对isa
指向做一个简单的总结
实例对象的
isa
指针指向对应的类对象,类对象的isa
指针指向对应的元类对象,元类对象的isa
指向基类的元类对象(根元类)。
可以简单的用lldb
调试来验证一下。
- (void)test {
//
NSObject *objc = [[NSObject alloc] init];
// isa指向的就是类对象
Class objcClass1 = [NSObject class];
Class objcMetaClass = object_getClass([NSObject class]);
}
先获取
objc
的isa
指针的值,然后用这个值与运算ISA_MASK
,得到的值,正好是类对象的地址。
然后用同样的方法获取类对象的值,和
ISA_MASK
与运算得到元类对象的地址。
可以继续这样再操作一轮,得到的还是元类对象的地址。这是因为该类是NSObject
,NSObject
元类对象的isa
是指向自己的(图中右上交的循环箭头)。
这里因为
0x00000001d64226c0
的高9位和低3位都是0所以没有做与运算,结果其实是一样的。
注意ISA_MASK
的使用需要注意环境,我这里是M1的模拟器。
superClass指向
superClass
的指向也在上面那个经典的图中。这里要注意superclass
是objc_class
才有的,所以实例对象是没有的。类对象的superclass
指向父类对象,元类对象的superclass
指向父类的元类对象,根元类对象的superclass
指针指向 根类对象。根类对象的superclass
是nil
。
注意:根元类对象的superclass
指针指向 根类对象,这个是有一个面试题的。
请听题:
@interface NSObject (Interview)
+ (void)foo;
@end
@implementation NSObject (Interview)
//能通过这个调用的原因是,NSObject的基元类的supclass为NSobject的元类
- (void)foo {
NSLog(@"IMP: -[NSObject foo]");
}
@end
- (void)test {
[NSObject foo];
}
上面的代码能执行成功么?这题要结合objc_msgSend
一起看,考察的点就是根元类对象的superclass
指针指向根类对象。
bits
类的结构中还有一个bits
,里面也存了很多信息,我们先看bits
的数据结构 class_data_bits_t
。
struct class_data_bits_t {
friend objc_class;
// Values are the FAST_ flags above.
uintptr_t bits;
public:
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
// Get the class's ro data, even in the presence of concurrent realization.
// fixme this isn't really safe without a compiler barrier at least
// and probably a memory barrier when realizeClass changes the data field
const class_ro_t *safe_ro() const {
class_rw_t *maybe_rw = data();
if (maybe_rw->flags & RW_REALIZED) {
// maybe_rw is rw
return maybe_rw->ro();
} else {
// maybe_rw is actually ro
return (class_ro_t *)maybe_rw;
}
}
}
其实最最重要的是其中的2个方法。data
和 safe_ro
,两个方法分别返回class_rw_t
和class_ro_t
。
这里ro_t
的获取也是通过data
方法获取的,所有可以理解为ro_t
也在rw_t
之中。
class_rw_t
继续来看class_rw_t
的数据结构。
在
rw_t
中可以 看到有这3个方法,通过这三个方法分别能获取到类的方法、属性、协议。
class_ro_t
再看class_ro_t
的数据结构。
可以看到有方法、属性、协议和成员变量。但方法、属性、协议的命名都是base
开通的。