id
id
可以表示任何 OC 中的对象,在 runtime 中对 id
是这么定义的
typedef struct objc_object *id;
所以 id
其实是一个指向 objc_object
结构体的指针类型,这也是为什么我们使用 OC 对象都需要添加 * ,而使用id对象的时候则不用。
既然 id
是指向结构体 objc_object
的指针类型,那么 objc_object
是什么?
其实在 OC 中 objc_object
就是表示对象,所以在 OC 中任何对象都是 C 中的结构体。所以从这里看就知道为什么 id
可以表示任何对象类型了。
对象
在 OC 中对象是由结构体 objc_object
表示的,那么这个结构体的定义
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
// Optimized calls to retain/release methods
id retain();
void release();
id autorelease();
};
为了方便观看其中省略了一些其他的东西。
可以看到结构体中除了一个变量 isa
就没有其他的变量了,还有一些其他常见的方法。所以其中的变量 isa
是什么东西呢?
其中 isa
是 isa_t
类型的。
isa_t
找到 isa_t
的定义
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if SUPPORT_NONPOINTER_ISA
// extra_rc must be the MSB-most field (so it matches carry/overflow flags)
// indexed must be the LSB (fixme or get rid of it)
// shiftcls must occupy the same bits that a real class pointer would
// bits + RC_ONE is equivalent to extra_rc + 1
// RC_HALF is the high bit of extra_rc (i.e. half of its range)
// future expansion:
// uintptr_t fast_rr : 1; // no r/r overrides
// uintptr_t lock : 2; // lock for atomic property, @synch
// uintptr_t extraBytes : 1; // allocated with extra bytes
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
struct {
uintptr_t indexed : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
};
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
struct {
uintptr_t indexed : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 8;
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
};
# else
// Available bits in isa field are architecture-specific.
# error unknown architecture
# endif
// SUPPORT_NONPOINTER_ISA
#endif
};
可以看到 isa_t
是一个 union
类型,就是里面的成员都共用一个内存,大小则由最大的数据项决定。
那么为什么要用 union
类型呢?因为自从苹果推出了 iPhone5s 采用了 64 位架构的 A7 双核处理器,如果只用来存储一个地址的话就会十分的浪费内存,所以苹果引入了 TaggedPointer 。TaggedPointer 就是让本来存储内存地址的指针一部分用来存储其他的数据,从而减少浪费。而对于 isa_t
就是使用一部分存储所指向的类的地址,而另一部分则存储像引用计数等其他数据。
从上面的关于 isa_t
的结构中可以看出很多的宏就是判断是否支持 TaggedPoint 从而决定存储方式。而判断这个 isa
指针是否是 TaggedPoint 就是看指针的标志位是否为1。
#if SUPPORT_MSB_TAGGED_POINTERS
# define TAG_MASK (1ULL<<63)
#else
# define TAG_MASK 1
inline bool
objc_object::isTaggedPointer()
{
#if SUPPORT_TAGGED_POINTERS
return ((uintptr_t)this & TAG_MASK);
#else
return false;
#endif
}
所以回过头来看 isa_t
其中除了储存了一下相关数据
Class
在 objc_object
结构体中有返回 Class
的函数
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
函数中返回的 Class
类型就是 OC 中的类
那么这个 Class
是什么,
typedef struct objc_class *Class;
可以看到 Class
是一个指向结构体 objc_class
的指针,找到结构体 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
class_rw_t *data() {
return bits.data();
}
//...
};
可以看到 objc_class
是继承自 objc_object
的,所以在 OC 中类其实也是一个对象。
所以在 objc_class
中有的成员是
- isa : 从
objc_object
中继承得到 - superclass : 指向父类
- cache : 表示缓存的数据
- bits : 里面存储了类的方法和成员变量等
当调用实例方法的时候,那么就从 objc_object
的 isa
中找到对象所属的类 ( objc_class
)然后在所属的类中搜索相对应得方法。如果没有找到那么就根据 objc_class
的 superclass
找到父类,在父类中搜索相对应的方法。
元类
我们上面提到,本质上 OC 中的类也是对象,它也是某个类的实例,这个类我们称之为元类(metaclass)。
因此,我们也可以通过调用类方法,比如 [NSObject new],给类对象发送消息。同样的,类对象能否响应这个消息也要通过 isa
找到类对象所属的类(元类)才能知道。也就是说,实例方法是保存在类中的,而类方法是保存在元类中的。
那元类也是对象吗?是的话那它又是什么类的实例呢?是的,没错,元类也是对象(元类对象),元类也是某个类的实例,这个类我们称之为根元类(root metaclass)。不过,有一点比较特殊,那就是所有的元类所属的类都是同一个根元类(当然根元类也是元类,所以它所属的类也是根元类,即它本身)。根元类指的就是根类的元类,具体来说就是根类 NSObject 对应的元类*。
因此,理论上我们也可以给元类发送消息,但是 OC 倾向于隐藏元类,不想让大家知道元类的存在。元类是为了保持 OC 对象模型在设计上的完整性而引入的,比如用来保存类方法等,它主要是用来给编译器使用的。
可以用一张图概括