OC的runtime是基于C语言实现的objective-C 的动态运行时机制,是iOS开发者最基础的知识,虽然互联网上相关的介绍多如牛毛,但是这种最基础的机制,每个开发者都需要有自己的理解与整理。
基本概念
第一、struct和union
由于runtime是基于C实现的,里面有大量的C预研数据结构,如果对C语言不太熟悉,理解起来未免费劲。所以,这里首先需要搞清楚这两个概念:
struct和union都是数据结构的标示方式,用于描述一种状态。二者最大区别是内存分配的差异:struct中的各个数据成员内存是单独分配的,而union内存只分配一份,各个数据成员共享。所以struct在遵循字节对齐的原则下,其内存是各个数据成员的总和,而union的内存大小是其数据成员中占内存最大的成员的内存大小。
下面这篇文章对Union的解释比较透彻:https://www.cnblogs.com/x_wukong/p/5912682.html
第二、OC的基础数据结构
Class: Class是一个指向objc_class结构体的指针
typedef struct objc_class *Class;
而objc_class结构体,苹果目前的实现是
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
大部分情况下,我们看到的资料对类结构的理解都是这样的:
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
但是上述实现OBJC2以前的。当前的实现其实只是(我只所以这么说,主要是目前最新的系统头文件还是这种定义)
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
} OBJC2_UNAVAILABLE;
即objc_class中只有isa指针,这里的isa指向实例所属的类。
而在最新的objc-runtime-new.h中,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
//结构体中的方法在这里省略了
}
可以看出objc_class继承自objc_object,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();
.....
}
这里我们先不展开描述objc_object的具体实现,但是可以确定是objc_class也是一种对象。
这里,我们有必要稍微深入一下最新的Class的实现,先看看cache的实现:
struct cache_t {
struct bucket_t *_buckets;
mask_t _mask;
mask_t _occupied;
public:
struct bucket_t *buckets();
mask_t mask();
mask_t occupied();
void incrementOccupied();
void setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask);
void initializeToEmpty();
mask_t capacity();
bool isConstantEmptyCache();
bool canBeFreed();
static size_t bytesForCapacity(uint32_t cap);
static struct bucket_t * endMarker(struct bucket_t *b, uint32_t cap);
void expand();
void reallocate(mask_t oldCapacity, mask_t newCapacity);
struct bucket_t * find(cache_key_t key, id receiver);
static void bad_cache(id receiver, SEL sel, Class isa) __attribute__((noreturn));
};
而_buckets的类型为
struct bucket_t {
private:
cache_key_t _key;
IMP _imp;
public:
inline cache_key_t key() const { return _key; }
inline IMP imp() const { return (IMP)_imp; }
inline void setKey(cache_key_t newKey) { _key = newKey; }
inline void setImp(IMP newImp) { _imp = newImp; }
void set(cache_key_t newKey, IMP newImp);
};
所以,这里_buckets中其实缓存的方法实现,很显然,这种机制可以提升方法的访问效率。
Class中另一个重要的成员为class_data_bits_t 类型的bits,它扮演了class存储数据的功能,bits中最重要的是class_rw_t结构的data,我们看看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
void setFlags(uint32_t set)
{
OSAtomicOr32Barrier(set, &flags);
}
void clearFlags(uint32_t clear)
{
OSAtomicXor32Barrier(clear, &flags);
}
// set and clear must not overlap
void changeFlags(uint32_t set, uint32_t clear)
{
assert((set & clear) == 0);
uint32_t oldf, newf;
do {
oldf = flags;
newf = (oldf | set) & ~clear;
} while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
}
};
这里我们看到了熟悉的methods,properties,protocols,但是没有看到ivars。但是还有一个const 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;
}
};
此结构体包含了实例的初始地址,实例大小,成员变量的布局,我们在这里看到ivars,且是const类型。那么这个ro为何物?回过头我们看看它是const修饰的,这部分其实是实例的不可变部分,或者说是类在编译期就确定的基本成分。
而methods,properties,protocols则可以在动态运行时修改。
id:OC中指向对象的类型,其定义为:
typedef struct objc_object *id;
它就是指向objc_object结构体的一个指针。
SEL:这是指向objc_selector结构体的一个指针
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
值得一提的是: struct objc_selector 的定义确实在runtime的源码中不可见,我们可以猜测其内部实现至少包含一个char指针,指向selector的名称。
IMP:方法实现指针
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);
#endif
从定义上看不是结构体,而是个函数指针,在老的定义中,相关变量的类型为void,而在新的定义中,相关的变量类型为id
以上是runtime中最基本的几个概念,了解他们是分析runtime源码的基础,特此mark