Runtime知识梳理

iOS Runtime知识梳理

源码可在这里查看:https://opensource.apple.com/tarballs/objc4/

一、isa的理解

  • 1、对象指向
    指向它的类对象,从而找到对象的方法。对象、类、元类的关系如下图所示:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iZehn30A-1614220082975)(media/16122575519768/16134507799189.jpg)]

  • 2、类型
    a、纯指针:指向内存地址
    b、NON_POINTER_ISA:指向内存地址和一些其他信息

二、class_rw_t和class_ro_t的理解

  • 1、class_rw_t:可读可写,存储objc类中的属性、方法列表、协议列表等
//官方源码
struct class_rw_t {
        // Be warned that Symbolication knows the layout of this structure.
        uint32_t flags;
        uint16_t witness;
    #if SUPPORT_INDEXED_ISA
        uint16_t index;
    #endif
        explicit_atomic<uintptr_t> ro_or_rw_ext;
        
        Class firstSubclass;
        Class nextSiblingClass;
    private:
        using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t *, 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}.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}.storeAt(ro_or_rw_ext, memory_order_release);
        }
    
        class_rw_ext_t *extAlloc(const class_ro_t *ro, bool deep = false);
    public:
        void setFlags(uint32_t set)
        {
            __c11_atomic_fetch_or((_Atomic(uint32_t) *)&flags, set, __ATOMIC_RELAXED);
        }
    
        void clearFlags(uint32_t clear) 
        {
            __c11_atomic_fetch_and((_Atomic(uint32_t) *)&flags, ~clear, __ATOMIC_RELAXED);
        }
    
        // 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));
        }
    
        class_rw_ext_t *ext() const {
            return get_ro_or_rwe().dyn_cast<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 *>();
            } else {
                return extAlloc(v.get<const class_ro_t *>());
            }
        }
    
        class_rw_ext_t *deepCopy(const class_ro_t *ro) {
            return extAlloc(ro, true);
        }
        //指向只读结构体,存储类初始信息
        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;
            }
            return v.get<const class_ro_t *>();
        }
    
        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 = 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 *>()) {
                return v.get<class_rw_ext_t *>()->methods;
            } else {
                return method_array_t{v.get<const class_ro_t *>()->baseMethods()};
            }
        }
        //属性列表
        const property_array_t properties() const {
            auto v = get_ro_or_rwe();
            if (v.is<class_rw_ext_t *>()) {
                return v.get<class_rw_ext_t *>()->properties;
            } else {
                return property_array_t{v.get<const class_ro_t *>()->baseProperties};
            }
        }
        //协议列表
        const protocol_array_t protocols() const {
            auto v = get_ro_or_rwe();
            if (v.is<class_rw_ext_t *>()) {
                return v.get<class_rw_ext_t *>()->protocols;
            } else {
                return protocol_array_t{v.get<const class_ro_t *>()->baseProtocols};
            }
        }
};
  • 2、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;
    
        // This field exists only when RO_HAS_SWIFT_INITIALIZER is set.
        _objc_swiftMetadataInitializer __ptrauth_objc_method_list_imp _swiftMetadataInitializer_NEVER_USE[0];
    
        _objc_swiftMetadataInitializer swiftMetadataInitializer() const {
            if (flags & RO_HAS_SWIFT_INITIALIZER) {
                return _swiftMetadataInitializer_NEVER_USE[0];
            } else {
                return nil;
            }
        }
        method_list_t *baseMethods() const {
            return baseMethodList;
        }
        class_ro_t *duplicate() const {
            if (flags & RO_HAS_SWIFT_INITIALIZER) {
                size_t size = sizeof(*this) + sizeof(_swiftMetadataInitializer_NEVER_USE[0]);
                class_ro_t *ro = (class_ro_t *)memdup(this, size);
                ro->_swiftMetadataInitializer_NEVER_USE[0] = this->_swiftMetadataInitializer_NEVER_USE[0];
                return ro;
            } else {
                size_t size = sizeof(*this);
                class_ro_t *ro = (class_ro_t *)memdup(this, size);
                return ro;
            }
        }
};

三、NSObject对像占用内存解析

  • 受内存分配机制的限制,一个NSObject对象都会分配16byte的内存。实际上,在64位系统下,只使用了8bytes;在32位系统下,只使用了4bytes。在iOS中,分配内存都是16的倍数。
#import <objc/runtime.h>
size_t size = class_getInstanceSize([NSObject class]);
NSLog(@"class instance size  %zu",size);
//打印输出结果
2021-02-16 13:42:33.664592+0800 DSPracticeDemo[38554:2390822] class instance size  8
====================================================================================
#import <malloc/malloc.h>
NSObject *obj = [[NSObject alloc] init];
    size_t mallocSize = malloc_size((__bridge const void *)obj);
 NSLog(@"class instance malloc size  %zu",mallocSize);
 打印输出结果
 2021-02-16 13:56:14.341374+0800 DSPracticeDemo[38624:2442458] class instance malloc size  16
  • class_getInstanceSize查看源码,本质如下
size_t class_getInstanceSize(Class cls)
{
        if (!cls) return 0;
        //调用内存对齐大小的方法
        return cls->alignedInstanceSize();
}

四、runtime方法缓存、存储形式、数据结构和查找方式

  • 方法缓存:cache_t增量扩展的哈希表结构,哈希表结构中存储的是bucket_t
  • 存储形式:bucket_t中存储的是SEL、IMP键值对
  • 数据结构:
    a、cache_t数据结构:
struct cache_t {
    #if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
        explicit_atomic<struct bucket_t *> _buckets;
        explicit_atomic<mask_t> _mask;
    #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
        explicit_atomic<uintptr_t> _maskAndBuckets;
        mask_t _mask_unused;
        ...
}

b、bucket_t数据结构:

struct bucket_t {
        private:
        // IMP-first is better for arm64e ptrauth and no worse for arm64.
        // SEL-first is better for armv7* and i386 and x86_64.
        #if __arm64__
        explicit_atomic<uintptr_t> _imp;
        explicit_atomic<SEL> _sel;
        #else
        explicit_atomic<SEL> _sel;
        explicit_atomic<uintptr_t> _imp;
        #endif
        ...
}
  • 查找方式:有序,采用二分查找法;无序,直接遍历

五、NSObject的dealloc释放机制

  • 1、调用release:引用计数变为0
  • 2、调用[self dealloc]
  • 3、父类调用dealloc(每一层继承都会调用)
  • 4、调用NSObject dealloc:在OC中只做一件事,就是在runtime中调用object_dispose方法处理C++相关的代码、引用计数表、弱引用表、关联对象等
//官方源码
//NSObject的dealloc方法调用_objc_rootDealloc方法
- (void)dealloc {
        _objc_rootDealloc(self);
}
void _objc_rootDealloc(id obj)
{
        ASSERT(obj);
        obj->rootDealloc();
}
inline void objc_object::rootDealloc()
{
        if (isTaggedPointer()) return;  // fixme necessary?
        //判断:isa.nonpointer、弱引用表、关联对象对象、C++代码和引用计数表等
        if (fastpath(isa.nonpointer  &&  
                     !isa.weakly_referenced  &&  
                     !isa.has_assoc  &&  
                     !isa.has_cxx_dtor  &&  
                     !isa.has_sidetable_rc))
        {
            assert(!sidetable_present());
            free(this);
        } 
        else {
            object_dispose((id)this);
        }
}
id object_dispose(id obj)
{
        if (!obj) return nil;
        objc_destructInstance(obj);    
        free(obj);
        return nil;
}
/***********************************************************************
* objc_destructInstance
* Destroys an instance without freeing memory. 
* Calls C++ destructors.
* Calls ARC ivar cleanup.
* Removes associative references.
* Returns `obj`. Does nothing if `obj` is nil.
**********************************************************************/
void *objc_destructInstance(id obj) 
{
        if (obj) {
            // Read all of the flags at once for performance.
            bool cxx = obj->hasCxxDtor();
            bool assoc = obj->hasAssociatedObjects();
            // This order is important.
            if (cxx) object_cxxDestruct(obj);
            if (assoc) _object_remove_assocations(obj);
            obj->clearDeallocating();
        }
        return obj;
}
inline void objc_object::clearDeallocating()
{
        if (slowpath(!isa.nonpointer)) {
            // Slow path for raw pointer isa.
            sidetable_clearDeallocating();
        }
        else if (slowpath(isa.weakly_referenced  ||  isa.has_sidetable_rc)) {
            // Slow path for non-pointer isa with weak refs and/or side table data.
            clearDeallocating_slow();
        }
        assert(!sidetable_present());
}
NEVER_INLINE void objc_object::clearDeallocating_slow()
{
        ASSERT(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc));
        SideTable& table = SideTables()[this];
        table.lock();
        if (isa.weakly_referenced) {
            weak_clear_no_lock(&table.weak_table, (id)this);
        }
        if (isa.has_sidetable_rc) {
            table.refcnts.erase(this);
        }
        table.unlock();
}

六、Method Swizzling(黑魔法)

  • 方法交换,在OC中调用一个方法,其实是向一个对象发送一个消息,查找消息的唯一依据是selector的名字。利用OC的动态特性,可以在运行时交换selector对应的实现。每个类中都有一个方法列表,存放着方法名和方法实现的映射关系,selector本质是方法名,imp是指向函数实现的指针,通过selector可以找到对应的imp,然后交换方法实现。
  • 通过这三个方法可以交换方法method_exchangeImplementations(交换两个方法的实现)、class_replaceMethod(替换方法实现)、method_setImplementation(设置方法实现)
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-83xVeQFX-1614220082977)(media/16122575519768/16135699387218.jpg)]

七、分类添加属性

  • 关联对象以哈希表的方式存储在全局单例中
@interface NSObject (Extension)
@property (nonatomic,copy) NSString *name;
@end
@implementation NSObject (Extension)
- (void)setName:(NSString *)name{
    objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY);
}
- (NSString *)name{
    return objc_getAssociatedObject(self, @selector(name));
}
@end

八、类对象数据结构

  • 数据结构相对丰富一些,它继承于objc_object
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
        //数据,封装好的class_rw_t
        class_rw_t *data() const {
            return bits.data();
        }
        ...
}

九、Category和原类合并时机、用途和实现原理

  • 合并时机
    1、程序启动,通过编译之后,runtime会进行初始化,调用__objc_init
    2、然后map_images
    3、接下来调用map_images_nolock
    4、read_images读取类中的所有信息
    5、然后调用reMethodizeClass重新方法化
    6、reMethodize中调用attachCategories将categories和原类合并

  • 用途
    1、给系统类添加方法、属性
    2、某个类有大量的方法,可实现根据名称进行分类

  • 实现原理
    在运行时,方法以倒序的方式插入到原类的方法列表中,所以不同的category,添加同一个方法,实际有效的是最后添加的方法


  • 转载请标明出处
  • 如有错误理解,还请各路大佬批评指出
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值