runtime深入解析
1.前言
OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行, 而OC的动态性是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关的函数,平时编写的OC代码,底层都是转换成了Runtime API进行调用。源码参考objc4-818.2。
2. 数据结构解析
2.1 Class
typedef struct objc_class *Class;
struct objc_object {
private:
isa_t isa;
...
}
struct objc_class : objc_object {
Class superclass;
cache_t cache;
class_data_bits_t bits;
...
class_rw_t *data() const {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
...
}
/*
objc_class 包含以下四个信息
1. isa_t isa,是一个普通的指针,存储着Class、Meta-Class对象的内存地址,以及一些其他的信息
2. Class superclass,指向父类
3. cache_t cache, 缓存调用了的方法,但不是所有的调用了的方法都存在这里
4. class_data_bits_t bits,存储着methodList、protocols、ivars、properties等信息
*/
union isa_t {
...
uintptr_t bits;
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 unused : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19
}
....
};
/*
isa_t 是一个联合体,联合体中的结构体是为了增强可读性的,下详细介绍一下结构体中每一位的意义。
1. nonpointer
0,代表普通的指针,存储着Class、Meta-Class对象的内存地址;
1,代表优化过,使用位域存储更多的信息;
2. has_assoc
是否有设置过关联对象,如果没有,释放时会更快
3. has_cxx_dtor
是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
4. shiftcls
存储着Class、Meta-Class对象的内存地址信息,比如 Class cls = bits & ISA_MASK, ISA_MASK = 0x0000000ffffffff8ULL, 也就是shiftcls
5. magic
用于在调试时分辨对象是否未完成初始化
6. weakly_referenced
是否有被弱引用指向过,如果没有,释放时会更快
7. deallocating
对象是否正在释放
8. extra_rc
里面存储的值是引用计数, 注意objc4-750版本是引用计数-1
9. has_sidetable_rc
引用计数器是否过大无法存储在isa中;
如果为1,extra_rc = RC_HALF(1<<18), 另外 RC_HALF+x 存放到sidetable中,
*/
struct class_data_bits_t {
friend objc_class;
// Values are the FAST_ flags above.
uintptr_t bits;
....
public:
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;
}
// Get the class's ro data, even in the presence of concurrent realization.
// fixme this isn't really safe without a compiler barrier at least
// and probably a memory barrier when realizeClass changes the data field
const class_ro_t *safe_ro() const {
class_rw_t *maybe_rw = data();
if (maybe_rw->flags & RW_REALIZED) {
// maybe_rw is rw
return maybe_rw->ro();
} else {
// maybe_rw is actually ro
return (class_ro_t *)maybe_rw;
}
}
...
};
struct class_rw_t {
...
explicit_atomic<uintptr_t> ro_or_rw_ext;
...
private:
/*
联合指针,默认数据是class_ro_t, 当加载分类、runtime动态添加方法、runtime添加property、runtime添加protocol时会创建class_rw_ext_t 来承载新加的数据,这时联合指针数据会变成class_rw_ext_t。
*/
using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t, class_rw_ext_t, PTRAUTH_STR("class_ro_t"), PTRAUTH_STR("class_rw_ext_t")>;
const ro_or_rw_ext_t get_ro_or_rwe() const {
return ro_or_rw_ext_t{ro_or_rw_ext};
}
void set_ro_or_rwe(const class_ro_t *ro) {
ro_or_rw_ext_t{ro, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_relaxed);
}
void set_ro_or_rwe(class_rw_ext_t *rwe, const class_ro_t *ro) {
// the release barrier is so that the class_rw_ext_t::ro initialization
// is visible to lockless readers
rwe->ro = ro;
ro_or_rw_ext_t{rwe, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_release);
}
public:
...
class_rw_ext_t *ext() const {
return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
}
/*
如果没有class_rw_ext_t, 创建调用extAlloc创建class_rw_ext_t
*/
class_rw_ext_t *extAllocIfNeeded() {
auto v = get_ro_or_rwe();
if (fastpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext);
} else {
return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
}
}
class_rw_ext_t *deepCopy(const class_ro_t *ro) {
return extAlloc(ro, true);
}
/*
创建class_rw_ext_t, 对方法列表、属性列表、协议列表进行拷贝
*/
class_rw_ext_t *extAlloc(const class_ro_t *ro, bool deepCopy)
{
runtimeLock.assertLocked();
auto rwe = objc::zalloc<class_rw_ext_t>();
rwe->version = (ro->flags & RO_META) ? 7 : 0;
method_list_t *list = ro->baseMethods();
if (list) {
if (deepCopy) {
list = list->duplicate()
};
rwe->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (proplist) {
rwe->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
rwe->protocols.attachLists(&protolist, 1);
}
set_ro_or_rwe(rwe, ro);
return rwe;
}
const class_ro_t *ro() const {
auto v = get_ro_or_rwe();
if (slowpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
}
return v.get<const class_ro_t *>(&ro_or_rw_ext);
}
void set_ro(const class_ro_t *ro) {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro = ro;
} else {
set_ro_or_rwe(ro);
}
}
const method_array_t methods() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {//创建了class_rw_ext_t
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
} else {//没有创建class_rw_ext_t
return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
}
}
const property_array_t properties() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) { //创建了class_rw_ext_t
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
} else { //没有创建class_rw_ext_t
return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
}
}
const protocol_array_t protocols() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {//创建了class_rw_ext_t
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
} else {//没有创建class_rw_ext_t
return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
}
}
};
struct class_rw_ext_t {
class_ro_t_authed_ptr<const class_ro_t> ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
...
};
struct class_ro_t {
...
void *baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
#if __has_feature(ptrauth_calls)
method_list_t *ptr = ptrauth_strip((method_list_t *)baseMethodList, ptrauth_key_method_list_pointer);
if (ptr == nullptr)
return nullptr;
// Don't auth if the class_ro and the method list are both in the shared cache.
// This is secure since they'll be read-only, and this allows the shared cache
// to cut down on the number of signed pointers it has.
bool roInSharedCache = objc::inSharedCache((uintptr_t)this);
bool listInSharedCache = objc::inSharedCache((uintptr_t)ptr);
if (roInSharedCache && listInSharedCache)
return ptr;
// Auth all other small lists.
if (ptr->isSmallList())
ptr = ptrauth_auth_data((method_list_t *)baseMethodList,
ptrauth_key_method_list_pointer,
ptrauth_blend_discriminator(&baseMethodList,
methodListPointerDiscriminator));
return ptr;
#else
return (method_list_t *)baseMethodList;
#endif
}
...
};
Class可视化的数据结构如上图,class_rw_t相比之前发生了一些变化,之前class_rw_t是直接引用class_ro_t, 但现在引用的是ro_or_rw_ext_t类型的联合指针,这么做是为了优化内存。
class_ro_t是只读的,存放的是编译期间就确定的字段信息;而class_rw_t是在 runtime 时才创建的,它会先将class_ro_t的内容拷贝一份,再将类的分类的属性、方法、协议等信息添加进去,之所以要这么设计是因为 Objective-C 是动态语言,你可以在运行时更改它们方法,属性等,并且分类可以在不改变类设计的前提下,将新方法添加到类中。苹果的优化就是把只读的class_ro_t数据存入Clean Memory, 把动态的数据提取出来,我们称之为class_rw_ext_t,存放在Dirty Memory。当没有创建class_rw_ext_t时,内存中只有一份class_ro_t数据,相比之前减少了一半,由于class_ro_t又是存放在Clean Memory,当收到内存警告时候,Apple是可以自己从内存移除的,当需要的时候再加载在到内存。
class_rw_ext_t创建的时机如下
1.加载分类时
2. runtime动态添加方法时
3. runtime添加property时
4. runtime添加protocol时
总结:当class_rw_ext_t没有创建的时候,ro_or_rw_ext_t指向的是class_ro_t,class_rw_ext_t,class_ro_t的数据会被拷贝到class_rw_ext_t中,ro_or_rw_ext_t会指向class_rw_ext_t。
Tips
Clean Memory:是一个应用常驻内存的只读内存页集,iOS能够安全地从磁盘中移除或重载。内存申请时将以下的这些看做是Clean的:
a.系统framework
b.程序的二进制可执行文件
c.内存映射文件
因为Clean Memory是只读的所以,应用程序可以共享framework以及library。
Dirty Memory: 是无法被系统主动移除的常驻内存部分
补充:OC的三种对象
-
Instance对象的内存分布如下
成员变量在Instance对象间是独立,方法在Instance对象间是共享的,所以Instance对象只保存成员变量的信息, 且和父类的一起布局。 -
Class对象的内存分布如下
-
Meta Class对象的内存分布如下
-
Instance对象、Class对象 、Meta Class对象之间的关系如下图
文字总结如下:
- instance的isa指向class;
- class的isa指向meta-class;
- meta-class的isa指向基类的meta-class;
- class的superclass指向父类的class,如果没有父类,superclass指针为nil;
- meta-class的superclass指向父类的meta-class,基类的meta-class的superclass指向基类的class;
- instance调用对象方法的轨迹,isa找到class,方法不存在,就通过superclass找父类;
- class调用类方法的轨迹,isa找meta-class,方法不存在,就通过superclass找父;
2.2 Ivar
typedef struct ivar_t *Ivar;
struct ivar_t {
...
int32_t *offset; //相对偏移
const char *name; //成员变量名字
const char *type; // 类型编码,通过@encode的指令,可以查看具体类型的编码
uint32_t size; //占用内存的大小
...
};
2.3 objc_property_t
typedef struct property_t *objc_property_t;
struct property_t {
const char *name; //属性名字
const char *attributes; //属性的信息
};
2.4 Method
typedef struct method_t *Method;
struct method_t {
...
//内存占用比较多的数据结构, 每一项存的是绝对地址
struct big {
SEL name; //方法名称或选择器。选择器是字符串,但是它们在所在的类中是唯一的
const char *types;//类型编码
IMP imp; //方法实现的函数指针
};
private:
bool isSmall() const {
return ((uintptr_t)this & 1) == 1;
}
//内存占用比较小的数据结构, 每一项存的是相对于该项的相对地址
struct small {
RelativePointer<const void *> name;
RelativePointer<const char *> types;
RelativePointer<IMP> imp;
};
small &small() const {
ASSERT(isSmall());
return *(struct small *)((uintptr_t)this & ~(uintptr_t)1);
}
//small模式下, swizzle之后需要使用到
IMP method_t::remappedImp(bool needsLock) const {
ASSERT(isSmall());
if (needsLock) {
mutex_locker_t guard(runtimeLock);
return method_t_remappedImp_nolock(this);
} else {
return method_t_remappedImp_nolock(this);
}
//small模式下, swizzle需要重新映射imp
void method_t::remapImp(IMP imp) {
ASSERT(isSmall());
runtimeLock.assertLocked();
/*
namespace objc {
static objc::LazyInitDenseMap<const method_t *, IMP> smallMethodIMPMap;
}
*/
auto *map = objc::smallMethodIMPMap.get(true);
(*map)[this] = imp;
}
public:
big &big() const {
ASSERT(!isSmall());
return *(struct big *)this;
}
SEL name() const {
if (isSmall()) {
return (small().inSharedCache()
? (SEL)small().name.get()
: *(SEL *)small().name.get());
} else {
return big().name;
}
}
const char *types() const {
return isSmall() ? small().types.get() : big().types;
}
IMP imp(bool needsLock) const {
if (isSmall()) {
IMP imp = remappedImp(needsLock);
if (!imp)
imp = ptrauth_sign_unauthenticated(small().imp.get(),
ptrauth_key_function_pointer, 0);
return imp;
}
return big().imp;
}
void setName(SEL name) {
if (isSmall()) {
ASSERT(!small().inSharedCache());
*(SEL *)small().name.get() = name;
} else {
big().name = name;
}
}
void setImp(IMP imp) {
if (isSmall()) {
remapImp(imp);
} else {
big().imp = imp;
}
}
};
static IMP method_t_remappedImp_nolock(const method_t *m) {
runtimeLock.assertLocked();
auto *map = objc::smallMethodIMPMap.get(false);
if (!map)
return nullptr;
auto iter = map->find(m);
if (iter == map->end())
return nullptr;
return iter->second;
}
template <typename T>
struct RelativePointer: nocopy_t {
int32_t offset;
T get() const {
if (offset == 0)
return nullptr;
uintptr_t base = (uintptr_t)&offset;
uintptr_t signExtendedOffset = (uintptr_t)(intptr_t)offset;
uintptr_t pointer = base + signExtendedOffset;
return (T)pointer;
}
};
- arm64下, Method变化对比如下图:
由于方法实现地址不会脱离当前库的地址范围的特性存在,所以实际上,方法列表并不需要使用 64 位的寻址范围空间。他们只需要能够在自己的库地址中查找引用函数地址即可,这些函数将始终在附近。所以我们可以使用 32 位相对偏移来代替绝对 64 位地址。
注意:在big模式下Method Swizzling替换的是 2 个方法函数指针指向,方法函数实现可以在任意地方实现,在small模式下,这样就无法工作了。解决的方式是使用一个全局的映射表管理映射关系,详细如下图:
small模式优点总结:
1. big模式下存放的都是绝对地址,但库的地址取决于动态链接库加载之后的位置,ASLR(Address space layout randomization 地址空间布局随机化)的存在,动态链接器需要修正真实的指针地址, 而small模式下无论将库加载到内存中的任何位置,偏移量始终是相同的,因此从加载后不需要进行修正指针地址;
2.small模式 它们可以保存在只读存储器中,这会更加的安全;
3.small模式 所需的内存量减少了一半
- Method Types
通过@encode的指令,可以查看数据类型的编码,详细的对照如下图:
有了对数据类型的编码认知,那Method Types又是什么呢?
@interface Person : NSObject
- (void)changName:(NSString *)name;
@end
@implementation Person
- (void)changName:(NSString *)name {
-
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Method method1 = class_getInstanceMethod([Person class], @selector(init));
NSLog(@"init method types: %s", method_getTypeEncoding(method1));
Method method2 = class_getInstanceMethod([Person class], @selector(changName:));
NSLog(@"changName method types: %s", method_getTypeEncoding(method2));
}
return 0;
}
控制输出如下:
2022-03-26 18:30:02.262658+0800 Test[95611:5337986] init method types: @16@0:8
2022-03-26 18:30:02.264240+0800 Test[95611:5337986] changName method types: v24@0:8@16
以changName:(v24@0:8@16)方法来详细的剖析, 具体如下图:
2.5 category
struct category_t {
const char *name;
classref_t cls;
WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;
WrappedPtr<method_list_t, PtrauthStrip> 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;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi) {
if (!isMeta) return instanceProperties;
else if (hi->info()->hasCategoryClassProperties()) return _classProperties;
else return nil;
}
protocol_list_t *protocolsForMeta(bool isMeta) {
if (isMeta) return nullptr;
else return protocols;
}
};
加载流程如下图:
2.6 关联
typedef DenseMap<const void *, ObjcAssociation> ObjectAssociationMap;
typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;
class AssociationsManager {
using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>;
static Storage _mapStorage;
public:
AssociationsManager() { AssociationsManagerLock.lock(); }
~AssociationsManager() { AssociationsManagerLock.unlock(); }
AssociationsHashMap &get() {
return _mapStorage.get();
}
static void init() {
_mapStorage.init();
}
};
class ObjcAssociation {
uintptr_t _policy;
id _value;
public:
ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
ObjcAssociation() : _policy(0), _value(nil) {}
ObjcAssociation(const ObjcAssociation &other) = default;
ObjcAssociation &operator=(const ObjcAssociation &other) = default;
ObjcAssociation(ObjcAssociation &&other) : ObjcAssociation() {
swap(other);
}
inline void swap(ObjcAssociation &other) {
std::swap(_policy, other._policy);
std::swap(_value, other._value);
}
inline uintptr_t policy() const { return _policy; }
inline id value() const { return _value; }
inline void acquireValue() {
if (_value) {
switch (_policy & 0xFF) {
case OBJC_ASSOCIATION_SETTER_RETAIN:
_value = objc_retain(_value);
break;
case OBJC_ASSOCIATION_SETTER_COPY:
_value = ((id(*)(id, SEL))objc_msgSend)(_value, @selector(copy));
break;
}
}
}
inline void releaseHeldValue() {
if (_value && (_policy & OBJC_ASSOCIATION_SETTER_RETAIN)) {
objc_release(_value);
}
}
inline void retainReturnedValue() {
if (_value && (_policy & OBJC_ASSOCIATION_GETTER_RETAIN)) {
objc_retain(_value);
}
}
inline id autoreleaseReturnedValue() {
if (slowpath(_value && (_policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE))) {
return objc_autorelease(_value);
}
return _value;
}
};
void _objc_associations_init()
{
AssociationsManager::init();
}
id _object_get_associative_reference(id object, const void *key)
{
ObjcAssociation association{};
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
AssociationsHashMap::iterator i = associations.find((objc_object *)object);
if (i != associations.end()) {
ObjectAssociationMap &refs = i->second;
ObjectAssociationMap::iterator j = refs.find(key);
if (j != refs.end()) {
association = j->second;
association.retainReturnedValue();
}
}
}
return association.autoreleaseReturnedValue();
}
void _object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
// This code used to work when nil was passed for object and key. Some code
// probably relies on that to not crash. Check and handle it explicitly.
// rdar://problem/44094390
if (!object && !value) return;
if (object->getIsa()->forbidsAssociatedObjects())
_objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
DisguisedPtr<objc_object> disguised{(objc_object *)object};
ObjcAssociation association{policy, value};
// retain the new value (if any) outside the lock.
association.acquireValue();
bool isFirstAssociation = false;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
if (value) {
auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
if (refs_result.second) {
/* it's the first association we make */
isFirstAssociation = true;
}
/* establish or replace the association */
auto &refs = refs_result.first->second;
//尝试插入,如果已经存在就不会插入
auto result = refs.try_emplace(key, std::move(association));
//之前已经存在,做替换
if (!result.second) {
association.swap(result.first->second);
}
} else {
auto refs_it = associations.find(disguised);
if (refs_it != associations.end()) {
auto &refs = refs_it->second;
auto it = refs.find(key);
if (it != refs.end()) {
//保留之前的值,为了做内存管理
association.swap(it->second);
refs.erase(it);
if (refs.size() == 0) {
associations.erase(refs_it);
}
}
}
}
}
// Call setHasAssociatedObjects outside the lock, since this
// will call the object's _noteAssociatedObjects method if it
// has one, and this may trigger +initialize which might do
// arbitrary stuff, including setting more associated objects.
if (isFirstAssociation)
object->setHasAssociatedObjects();
// release the old value (outside of the lock).
association.releaseHeldValue();
}
// Unlike setting/getting an associated reference,
// this function is performance sensitive because of
// raw isa objects (such as OS Objects) that can't track
// whether they have associated objects.
void _object_remove_assocations(id object, bool deallocating)
{
ObjectAssociationMap refs{};
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
AssociationsHashMap::iterator i = associations.find((objc_object *)object);
if (i != associations.end()) {
//两个map互换
refs.swap(i->second);
// If we are not deallocating, then SYSTEM_OBJECT(系统自己的创建的) associations are preserved.
bool didReInsert = false;
if (!deallocating) {
for (auto &ref: refs) {
if (ref.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
i->second.insert(ref);
didReInsert = true;
}
}
}
if (!didReInsert)
associations.erase(i);
}
}
// Associations to be released after the normal ones.
SmallVector<ObjcAssociation *, 4> laterRefs;
// release everything (outside of the lock).
for (auto &i: refs) {
if (i.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
// If we are not deallocating, then RELEASE_LATER associations don't get released.
if (deallocating)
laterRefs.append(&i.second);
} else {
i.second.releaseHeldValue();
}
}
for (auto *later: laterRefs) {
later->releaseHeldValue();
}
}
可视化的数据结构如下图:
2.10 cache_t
struct cache_t {
...
/*
真机arm64下低44位保留buckets,buckets是个散列表, 高16位保存mask, mask = buckets的capacity * 2
*/
explicit_atomic<uintptr_t> _bucketsAndMaybeMask;
union {
struct {
/*
真机arm64 下不会使用
*/
explicit_atomic<mask_t> _maybeMask;
#if __LP64__
uint16_t _flags;
#endif
/*
已缓存的方法数量
*/
uint16_t _occupied;
};
};
...
}
struct bucket_t {
explicit_atomic<uintptr_t> _imp;
explicit_atomic<SEL> _sel;
}
insert流程如下图:
3.消息机制的三个阶段
全景图如下:(消息发送 -> 动态方法解析 -> 消息转发)
4. 运用
- json和model之间的转换
- 自动归解档
- KVO
- 给分类添加属性
- Add智能方法,防止抛doesNotRecognizeSelector: 异常
- Hook