runtime深入解析

33 篇文章 1 订阅
23 篇文章 0 订阅

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对象之间的关系如下图
    ​​在这里插入图片描述
    文字总结如下:

  1. instance的isa指向class;
  2. class的isa指向meta-class;
  3. meta-class的isa指向基类的meta-class;
  4. class的superclass指向父类的class,如果没有父类,superclass指针为nil;
  5. meta-class的superclass指向父类的meta-class,基类的meta-class的superclass指向基类的class;
  6. instance调用对象方法的轨迹,isa找到class,方法不存在,就通过superclass找父类;
  7. 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
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值