当不肯定一个对象的类型的时候,一般使用 id
类型进行表示数组
- (id)copy;
复制代码
id
表明一个对象,它是指向一个实例对象的指针缓存
typedef struct objc_object *id;
复制代码
实际上,id
是一个 objc_object
结构体类型指针的别名bash
struct objc_object {
isa_t isa;
};
复制代码
而 objc_object
这个结构体中只有一个 isa_t
类型的成员 isa
,它包含了当前对象所属于的类的信息。架构
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
复制代码
isa_t
是一个联合体,这就意味着 isa_t
中保存的既能够是一个 Class
类型的指针,也能够是一个 64
位的 bits
,但在某一个时刻,只能保存两者中的一个。ide
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
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
# define ISA_BITFIELD \
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)
复制代码
在不一样架构下 bits
内存布局有所不一样,可是结构体中的成员和其表明的含义都是相同的,只是具体结构体的实现和位数可能有些差异。函数
inline void
objc_object::initClassIsa(Class cls)
{
if (DisableNonpointerIsa || cls->instancesRequireRawIsa()) {
initIsa(cls, false/*not nonpointer*/, false);
} else {
initIsa(cls, true/*nonpointer*/, false);
}
}
inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
initIsa(cls, true, hasCxxDtor);
}
复制代码
不管是初始化类仍是实例的 isa
都是调用了下面的方法布局
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
if (!nonpointer) {
isa = isa_t((uintptr_t)cls);
} else {
isa_t newisa(0);
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
isa = newisa;
}
}
复制代码
若是是老版本 isa
中就是保存 Class
类型的指针。若是是新版本,就对 64
位的 bits
进行配置ui
其中 ISA_MAGIC_VALUE
实际上配置告终构体中成员 nonpointer
和 magic
的值。this
nonpointer
若是是 0
那么 isa
中就是保存 Class
类型的指针,若是是 1
就保存的是 bits
。magic
用于调试器判断当前对象是真的对象仍是没有初始化的空间接着配置了 has_cxx_dtor
的值,该值表示当前对象有 C++ 或者 ObjC 的析构器(destructor),若是没有析构器就会快速释放内存。atom
最后将类的指针右移了 3 位后赋值给了成员 shiftcls
。这是由于类的指针是按照字节(8bits)对齐的,其指针后三位都是没有意义的 0,所以能够右移 3 位进行消除,以减少无心义的内存占用。
成员名 | 含义 |
---|---|
nonpointer | 区分新老版本,老版本是 Class cls ,新版本是 uintptr_t bits |
has_assoc | 对象含有或者曾经含有关联引用,没有关联引用的能够更快地释放内存 |
has_cxx_dtor | 对象有 C++ 或者 ObjC 的析构器(destructor),若是没有析构器就会快速释放内存 |
shiftcls | 类的指针 |
magic | 调试器判断当前对象是真的对象仍是没有初始化的空间 |
weakly_referenced | 对象被指向或者曾经指向一个 ARC 的弱变量,没有弱引用的对象能够更快释放 |
deallocating | 对象正在释放内存 |
has_sidetable_rc | 对象的引用计数太大了,存不下 |
extra_rc | 对象的引用计数超过 1,会存在这个这个里面,若是引用计数为 10,该值就为 9 |
在获取一个对象所属的类时,返回值的类型是 Class
- (Class)class;
复制代码
Class
表明一个类,它是指向一个类对象的指针
typedef struct objc_class *Class;
复制代码
实际上,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
};
复制代码
能够看到 objc_class
中有三个成员。
首先,objc_class
是继承自 objc_object
,也就是说类也是一个对象。其中从 objc_object
继承下来的成员 isa
,它包含了当前类对象所属的元类的信息。
其次,成员 superclass
指向了该类的父类
接着,成员 cache
中缓存了最近调用过的方法
最后,成员 bits
根据注释能够知道 bits
= 指向 class_rw_t 类型结构体的指针
+ 自定义的 rr/alloc 标识
struct cache_t {
explicit_atomic<struct bucket_t *> _buckets;
explicit_atomic<mask_t> _mask;
uint16_t _occupied;
};
复制代码
用来缓存调用过方法的是一个 cache_t
类型的结构体,共有三个成员。
第一个成员 _buckets
是一个 bucket_t
类型的数组。
第二个成员 _mask
保存着总共申请用来缓存的数量。
第三个成员 _occupied
保存着目前已经使用缓存的数量。
struct bucket_t {
#if __arm64__
explicit_atomic<uintptr_t> _imp;
explicit_atomic<SEL> _sel;
#else
explicit_atomic<SEL> _sel;
explicit_atomic<uintptr_t> _imp;
#endif
};
复制代码
bucket_t
结构体中存储了键值对,其中 _imp
是一个函数指针,指向函数的具体实现,_sel
是方法名或者方法标识。也就说 _buckets
是一个哈希表,里面经过键值对的方式保存着调用过的方法。键为 SEL
类型,值为 IMP
类型。
typedef struct objc_selector *SEL;
复制代码
从声明上看 SEL
是一个 objc_selector
结构体指针的别名;从实现中找,发现 SEL
是指向一个 char *
变量,也就是说其实它就是个映射到方法的 C 字符串,能够理解为是区分方法的 ID。
SEL
只与方法名有关:
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);
#endif
复制代码
IMP
是一个函数指针,它指向函数的具体实现。
bits
中保存着类的相关信息,它是一个 class_data_bits_t
类型的结构体,
struct class_data_bits_t {
// Values are the FAST_ flags above.
uintptr_t bits;
};
复制代码
这个结构体中的成员和 isa_t
结构体相同,都是经过一个 64
位的 bits
储存信息
// class is a Swift class from the pre-stable Swift ABI
#define FAST_IS_SWIFT_LEGACY (1UL<<0)
// class is a Swift class from the stable Swift ABI
#define FAST_IS_SWIFT_STABLE (1UL<<1)
// class or superclass has default retain/release/autorelease/retainCount/
// _tryRetain/_isDeallocating/retainWeakReference/allowsWeakReference
#define FAST_HAS_DEFAULT_RR (1UL<<2)
// data pointer
#define FAST_DATA_MASK 0x00007ffffffffff8UL
复制代码
经过定义的宏能够知道这些位都表明了什么含义
FAST_IS_SWIFT_LEGACY
是不是来自预稳定 Swift ABI 的 Swift 类FAST_IS_SWIFT_STABLE
是不是来自已稳定 Swift ABI 的 Swift 类FAST_HAS_DEFAULT_RR
当前类或者父类含有默认的 retain/release/autorelease/retainCount/_tryRetain/_isDeallocating/retainWeakReference/allowsWeakReference 方法FAST_DATA_MASK
数据指针搜索 FAST_DATA_MASK
的使用位置,能够找到一对取值/赋值方法
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
void setData(class_rw_t *newData)
{
ASSERT(!data() || (newData->flags & (RW_REALIZING | RW_FUTURE)));
// Set during realization or construction only. No locking needed.
// Use a store-release fence because there may be concurrent
// readers of data and data's contents. uintptr_t newBits = (bits & ~FAST_DATA_MASK) | (uintptr_t)newData; atomic_thread_fence(memory_order_release); bits = newBits; } 复制代码
经过这两方法,能够知道数据指针是一个 class_rw_t
类型的指针
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t version;
uint16_t witness;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
复制代码
结构体 class_rw_t
名称中的 rw
表明 readwrite
,其中的成员 ro
是一个 class_ro_t
类型的指针,这里的 ro
表明 readonly
。ro
中存储了当前类在编译期就已经肯定的属性、方法以及遵循的协议。而 class_rw_t
提供了运行时对类拓展的能力,其中的 methods
、properties
、protocols
保存着经过 Category 在运行时添加的方法、属性及协议。
成员 methods
、properties
、protocols
对应的 method_array_t
、property_array_t
、protocol_array_t
类型都是继承自 list_array_tt<Element, List>
,该类型能够保存三种类型的值:
经过第 3 种类型也就是二维数组类型,能够实现数据的扩展。例如,method_array_t
是一个数组,其中保存的元素是 method_list_t
,而 method_list_t
也是一个数组,其中保存的元素是 method_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;
};
复制代码
结构体中的成员 baseMethodList
、baseProperties
、baseProtocols
和 ivar_list_t
对应的 method_list_t
、property_list_t
、protocol_list_t
和 ivar_list_t
类型都是继承自 entsize_list_tt
,一个泛型的数组结构,没有扩展功能。
类在运行时第一次初始化时会调用 realizeClass...
系列方法
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 = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}
复制代码
在该方法的实现中,能够知道在编译期间类的结构中的 class_data_bits_t *data
指向的是一个 class_ro_t *
。在运行时的时候调用该初始化方法时,首先为 class_rw_t *
申请内存空间,而后将 class_ro_t *
赋值给 rw->ro
,接着设置 class_rw_t *
的标志位标识状态,最后将建立好的 class_rw_t *
赋值给类结构。
Category 提供了在运行时动态的向已经存在的类中添加方法、协议和属性的功能
typedef struct category_t *Category;
复制代码
struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties;
};
复制代码
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
int flags)
{
constexpr uint32_t ATTACH_BUFSIZ = 64;
method_list_t *mlists[ATTACH_BUFSIZ];
uint32_t mcount = 0;
bool fromBundle = NO;
bool isMeta = (flags & ATTACH_METACLASS);
auto rw = cls->data();
for (uint32_t i = 0; i < cats_count; i++) {
auto& entry = cats_list[i];
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
if (mcount == ATTACH_BUFSIZ) {
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
rw->methods.attachLists(mlists, mcount);
mcount = 0;
}
mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
fromBundle |= entry.hi->isBundle();
}
}
if (mcount > 0) {
prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
rw->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
if (flags & ATTACH_EXISTING) flushCaches(cls);
}
}
复制代码
原来的方法中包含了对分类中方法、代理和属性的处理,但原理相同,为了减小篇幅,只保留对方法的处理:
method_list_t *
的二维数组类型的临时变量 mlists
mlist
,从后向前的添加到 mlists
中mlists
数组在遍历过程当中存满,则合并到 rw->methods
中,不然等待遍历完合并void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
}
else {
// 1 list -> many lists
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
复制代码
在合并时,大体有两种状况。一种是当前数组中为空,那就直接指向新添加的数组;另外一种是当前数组中已经有数据:
realloc()
方法为原数组扩充空间memmove()
方法移动到新申请空间的后面memcpy()
方法把新数组复制到新空间的前面经过上面两段代码,能够发现两点: