咱们都知道,Runtime 是 Objective-C 这门动态语言的核心,只有理解了它,咱们才可以更好的理解 Objective-C 究竟是如何工做的,在编程时,也会更加驾轻就熟。因为时间和精力有限,这次我主要想从如下几方面来进行 Runtime 源码的阅读,往后将会逐步完善。因为整体篇幅较长,因此我将会每一部分拆分红一篇文章来具体分析。html
1、对象的本质,了解 isa编程
2、对象的生命周期数组
3、对象的引用计数缓存
4、对象的扩展方法app
5、Runtime 的应用ide
/// Represents an instance of a class.
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
};
// 其内部有一个指向 Class 的指针,而 Class 是什么呢
// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
// 再来看一下 objc_class 的定义,须要注意的是,objc_class 也是继承自 objc_object, 因为 objc_object 中已经定义了一个 isa 指针,因为结构体中全部的成员都是 public 的,因此 objc_class 也就拥有了 isa 而且也拥有访问 isa 的权限。
struct objc_class : objc_object {
// Class ISA; // 此时,类中的 isa 指针指向的是 metaClass 元类
Class superclass; // 父类
cache_t cache; // formerly cache pointer and vtable 类的方法缓存,由于 Runtime 时会把第一次遇到的方法缓存到方法缓存中,此后将直接从缓存中读取方法,极大提升了效率
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
// class_data_bits_t <==> class_rw_t + 一个 rr/alloc 位
};
复制代码
因此到这里,咱们也就理解到了,实际上,类也是一个对象源码分析
咱们须要知道的是,在 Objective-C 中,对象的方法并没有存储于对象的结构体中(若是每个对象都保存了本身能执行的方法,那么对内存的占用有极大的影响)。在调用实例方法时,而是经过 isa 指针来寻找相应的类,经过 class_data_bits_t 来寻找类中的方法。具体是如何寻找的,咱们看👇ui
// 首先 class_data_bits_t 中有一个 bits 位
struct class_data_bits_t {
// Values are the FAST_ flags above.
uintptr_t bits;
public:
// 这里返回的数据是 class_rw_t* 指针类型的数据,在这个方法中咱们能够看出,将 bits 与 FAST_DATA_MASK 进行位运算,只取其中的 [3, 47] 位转换成 class_rw_t * 返回。
class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
}
// data() 返回的是一个 class_rw_t* 指针, class_rw_t 又是什么?
// 类中的属性、方法还有遵循的协议等信息都保存在 class_rw_t 中:
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
#if SUPPORT_INDEXED_ISA
uint32_t index;
#endif
};
// 由此咱们能够看出, class_rw_t 中包含了一些关于类的信息,好比 flag, 版本号, 方法数组, 属性数组等。而其中又有一个指向 class_ro_t 的指针, class_ro_t 又是什么?
// 原来,class_ro_t 中存储了当前类在编译期就已经肯定的属性、方法以及遵循的协议。
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
复制代码
因此由此,咱们知道 class_ro_t 保存的是在编译期时就已经肯定的方法,因此当在编译期时, class_data_bits_t 将直接指向 class_ro_t ,然后在 Runtime 时,将会调用 class_data_bits_t 的 data() 直接将结果从 class_rw_t 转化成 class_ro_t 指针, 而后再初始化一个 class_rw_t 指针,此时它中的数据都为空,而后再设置它的 ro 变量和 flag, 最后再为其设置正确的 datathis
/*********************************************************************** * realizeClass * Performs first-time initialization on class cls, * including allocating its read-write data. * Returns the real class structure for the class. * Locking: runtimeLock must be write-locked by the caller **********************************************************************/
static Class realizeClass(Class cls) {
runtimeLock.assertWriting();
const class_ro_t *ro;
class_rw_t *rw;
Class supercls;
Class metacls;
bool isMeta;
if (!cls) return nil;
if (cls->isRealized()) return cls;
assert(cls == remapClass(cls));
// fixme verify class is not in an un-dlopened part of the shared cache?
// 强制转化为 ro
ro = (const class_ro_t *)cls->data();
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro;
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
// 这时候首先给 rw 分配内存空间而且初始化为 0
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
// 使 rw 指向 ro
rw->ro = ro;
// 设置 rw 的 flag 为 正在初始化和已经初始化
/** // class is realized - must never be set by compiler #define RO_REALIZED (1<<31) // Values for class_rw_t->flags // These are not emitted by the compiler and are never used in class_ro_t. // Their presence should be considered in future ABI versions. // class_t->data is class_rw_t, not class_ro_t #define RW_REALIZED (1<<31) */
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}
// 判断是否为 metaClass RO_META (1 << 0)
isMeta = ro->flags & RO_META;
rw->version = isMeta ? 7 : 0; // old runtime went up to 6
// Choose an index for this class.
// Sets cls->instancesRequireRawIsa if indexes no more indexes are available
cls->chooseClassArrayIndex();
if (PrintConnecting) {
_objc_inform("CLASS: realizing class '%s'%s %p %p #%u",
cls->nameForLogging(), isMeta ? " (meta)" : "",
(void*)cls, ro, cls->classArrayIndex());
}
// Realize superclass and metaclass, if they aren't already.
// This needs to be done after RW_REALIZED is set above, for root classes.
// This needs to be done after class index is chosen, for root metaclasses.
supercls = realizeClass(remapClass(cls->superclass));
metacls = realizeClass(remapClass(cls->ISA()));
#if SUPPORT_NONPOINTER_ISA
// Disable non-pointer isa for some classes and/or platforms.
// Set instancesRequireRawIsa.
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
if (DisableNonpointerIsa) {
// Non-pointer isa disabled by environment or app SDK version
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && !(ro->flags & RO_META) &&
0 == strcmp(ro->name, "OS_object"))
{
// hack for libdispatch et al - isa also acts as vtable pointer
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->superclass &&
supercls->instancesRequireRawIsa())
{
// This is also propagated by addSubclass()
// but nonpointer isa setup needs it earlier.
// Special case: instancesRequireRawIsa does not propagate
// from root class to root metaclass
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsa(rawIsaIsInherited);
}
// SUPPORT_NONPOINTER_ISA
#endif
// Update superclass and metaclass in case of remapping
cls->superclass = supercls;
cls->initClassIsa(metacls);
// Reconcile instance variable offsets / layout.
// This may reallocate class_ro_t, updating our ro variable.
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
// Set fastInstanceSize if it wasn't set already.
cls->setInstanceSize(ro->instanceSize);
// Copy some flags from ro to rw
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
// Connect this class to its superclass's subclass lists
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// Attach categories
// 在这个方法中将 rw 的方法列表,属性列表,协议列表赋值
methodizeClass(cls);
return cls;
};
复制代码
此时,通过 Runtime 的做用以后,如今内存中的关系是,类中的 data 指针指向 class_data_bits_t, class_data_bits_t 结构体中的 data() 方法获取到的是 class_rw_t 指针, class_rw_t 结构体中的 ro 指针指向 class_ro_t。图以下:atom
可是问题来了,类的方法是如何被查找和调用的呢?因为咱们已经知道了,在 ObjC 中,实际上类也是一个特殊的对象,查找类的方法实际上就和查找实例方法采用一样的机制,可是如何才能让他们采用一样的机制呢?这时,元类的做用就显现了出来。
isa
在类中获取方法的实现isa
在元类中获取方法的实现咱们在 Runtime 的源码中能够看到,在不一样的处理器上,这个共同体所分配的内存位数是不一样的。
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if SUPPORT_PACKED_ISA
// extra_rc must be the MSB-most field (so it matches carry/overflow flags)
// nonpointer 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 nonpointer : 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 nonpointer : 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)
};
};
复制代码
以 x86_64_ 为例,
更深一步,从 isa 的初始化来看 isa 的结构
inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
initIsa(cls, true, hasCxxDtor);
}
inline void
objc_object::initIsa(Class cls, bool indexed, bool hasCxxDtor)
{
if (!indexed) {
isa.cls = cls;
} else {
isa.bits = ISA_MAGIC_VALUE;
isa.has_cxx_dtor = hasCxxDtor;
isa.shiftcls = (uintptr_t)cls >> 3;
}
}
// 因为对象的 isa 初始化时传入 indexed 为 true ,因此,可简化为
inline void
objc_object::initIsa(Class cls, bool indexed, bool hasCxxDtor)
{
// 其中 ISA_MAGIC_VALUE 为 0x000001a000000001ULL
isa.bits = ISA_MAGIC_VALUE;
isa.has_cxx_dtor = hasCxxDtor;
isa.shiftcls = (uintptr_t)cls >> 3;
}
复制代码
此时,当执行完 isa.bits = ISA_MAGIC_VALUE; 后 isa 的结构为 ,能够看到 ISA_MAGIC_VALUE 将 magic 和 indexed 都初始化了
接着 isa.has_cxx_dtor = hasCxxDtor; 这一位会设置 has_cxx_dtor 的值,若是是 1, 则表示当前对象是否有析构器,若是没有,就会快速释放
最后, isa.shiftcls = (uintptr_t)cls >> 3; 将当前对象对应的类指针赋值给 shiftcls 这些位,之因此向右移三位,移三位的主要缘由是用于将 Class 指针中无用的后三位清楚减少内存的消耗,由于类的指针要按照字节(8 bits)对齐内存,其指针后三位都是没有意义的 0。赋值以后以下
至此,也就证实了咱们以前对于初始化 isa
时对 initIsa
方法的分析是正确的。它设置了 indexed
、magic
以及 shiftcls
。
因为咱们如今使用告终构体 isa_t 来替代 Class 类型的指针, 因此咱们也就须要一个指针可以返回 isa 所指的类,因此咱们此时须要一个 ISA() 方法。
inline Class
objc_object::ISA()
{
assert(!isTaggedPointer());
#if SUPPORT_INDEXED_ISA
if (isa.nonpointer) {
uintptr_t slot = isa.indexcls;
return classForIndex((unsigned)slot);
}
return (Class)isa.bits;
#else
return (Class)(isa.bits & ISA_MASK);
#endif
}
// 简化后以下
inline Class
objc_object::ISA()
{
assert(!isTaggedPointer());
// 由此能够看到,实际上 ISA() 返回的是 isa.bits 与 0x0000000ffffffff8ULL 进行的按位与操做,确实能够返回当前的类
return (Class)(isa.bits & ISA_MASK);
}
复制代码