iOS中isa
指针是Objective-C对象内部的一个重要概念,它是实现对象与类之间关系的核心机制。深入理解isa
指针对掌握Objective-C的底层运行机制和对象模型非常重要。
1. 什么是isa指针
每个Objective-C对象都有一个isa
指针,它指向这个对象所属的类。类本身也有一个isa
指针,指向其元类(metaclass)。元类本身又有一个isa
指针,指向根元类,通常是NSObject
的元类。
isa
指针的优化:非指针 isa
在现代 64 位架构中,isa
指针被设计成一个压缩的位域结构,以便更多的编码信息可以直接存储在 isa
指针中,而不仅仅是一个指向类对象的指针。
64 位系统下 isa
的优化结构
以 Apple 的开源 Objective-C 运行时为例,在 64 位系统上,具体的位域结构如下:
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
struct {
uintptr_t nonpointer : 1; // 指示是否启用了优化版 isa
uintptr_t has_assoc : 1; // 是否有 associated 对象
uintptr_t has_cxx_dtor : 1; // 是否有 C++ 析构函数
uintptr_t shiftcls : 33; // 实际的类指针
uintptr_t magic : 6; // 调试时用的所谓的“魔数”
uintptr_t weakly_referenced : 1; // 是否被弱引用
uintptr_t deallocating : 1; // 是否正在被销毁
uintptr_t has_sidetable_rc : 1; // 是否拥有引用计数
uintptr_t extra_rc : 19; // 一部分的引用计数
};
};
2. isa
指针的实际用途
- 类信息存储: 通过
isa
指针,运行时系统可以找到对象的类,并获取类的方法列表、属性列表等。 - 类型检查和方法调用: 运行时通过
isa
指针实现类型检查和方法调用机制(动态绑定)。
在运行时,isa
指针用于以下几种目的:
2.1 动态类型识别
通过 isa
指针,运行时能够识别对象的实际类型。这对于动态消息传递系统至关重要。
- (void)someMethod {
if ([self isKindOfClass:[NSString class]]) {
NSLog(@"This object is a NSString or subclass");
}
}
2.2 方法分派
Objective-C 运行时通过 isa
指针找到类对象,然后在类对象的方法缓存和方法列表中搜索方法实现。这就是动态绑定的机制。
void objc_msgSend(id self, SEL _cmd, ...) {
Class cls = object_getClass(self);
// 查找方法实现
IMP imp = class_getMethodImplementation(cls, _cmd);
// 调用方法实现
imp(self, _cmd, ...);
}
3. isa
的非指针优化和引用计数
在非指针 isa
优化下,isa
中存储了引用计数的一部分,通过以下三个字段表示:
- extra_rc: 额外的引用计数。当引用计数超过某个范围时,将额外的计数存储在
extra_rc
中。 - has_sidetable_rc: 当引用计数超出了
extra_rc
能表示的范围时,会使用 SideTable 来存储更多的引用计数。
简化引用计数操作的方法:
增加引用计数
void objc_retain(id obj) {
if (obj) {
if (!obj->isa.nonpointer) {
// 传统方式增加引用计数
// objc::UnretainedUnsafe<objc_object> unretainedObject(obj);
obj->retainCount++;
} else {
// 使用非指针 isa 增加引用计数
if ((obj->isa.extra_rc) < 0xFFFFFF) {
obj->isa.extra_rc++;
} else {
// 使用 SideTable 计数
// sidetable_retain(obj);
}
}
}
}
减少引用计数
void objc_release(id obj) {
if (obj) {
if (!obj->isa.nonpointer) {
// 传统方式减少引用计数
// objc::UnretainedUnsafe<objc_object> unretainedObject(obj);
obj->retainCount--;
} else {
// 使用非指针 isa 减少引用计数
if (obj->isa.extra_rc > 0) {
obj->isa.extra_rc--;
} else {
// 使用 SideTable 计数
// sidetable_release(obj);
}
}
}
}
4. 结合源码理解 isa
指针
类与元类结构
Objective-C 的类结构与元类结构紧密相关,每个元类的 isa
都指向根元类,而根元类的 isa
指针又指向自身。
Class myClass = [NSObject class];
Class myMetaClass = object_getClass(myClass);
// 打印类信息
NSLog(@"Class: %@, MetaClass: %@", myClass, myMetaClass);
// 检查根元类
Class rootMetaClass = object_getClass(myMetaClass);
NSLog(@"RootMetaClass: %@", rootMetaClass);
// 元类的 isa 指针会指向根元类
NSLog(@"RootMetaClass isa: %@", *(Class *)((intptr_t)rootMetaClass & ~(1ULL<<62)));
使用 lldb
调试 isa
指针
可以使用 lldb
调试器查看对象的 isa
指针和相关信息,进一步理解底层结构:
(lldb) po [myObject class]
(lldb) p/x myObject->isa
总结
isa
指针是 Objective-C 运行时的核心机制之一,通过理解其内存布局和底层实现,我们可以更深入地了解动态类型系统、方法分派机制以及引用计数优化。这些知识有助于我们编写更高效、更可靠的 Objective-C 代码,同时也为调试和性能优化提供了强有力的工具。