美文网首页移动开发干货店程序员
NSObject 底层原理分析(一)

NSObject 底层原理分析(一)

作者: 星辰_入海 | 来源:发表于2022-02-21 23:13 被阅读0次

    @[TOC](NSObject 底层原理分析)
    NSObject在Objective-C中大多数类层次结构的根类,通常我们在使用NSObject对象时,会使用[[NSObject alloc] init] 或者 [NSObject new]创建对象实例,通过这篇文章,我们一起研究关于NSObject的对象创建过程。

    初始化

    调用alloc方式

    当我们调用[NSObject alloc]方法时候,会调用_objc_rootAlloc(self)这个方法,将NSObject自身作为参数传递下去申请一份内存。

    callAlloc 接受三个参数:

    1. 类;
    2. 是否检查为空;
    3. 是否调用allocWithZone;

    define ALWAYS_INLINE inline attribute((always_inline))

    采用内联函数

    id _objc_rootAlloc(Class cls)
    {
        return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
    }
    
    static ALWAYS_INLINE id
    callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
    {
    #if __OBJC2__
        if (slowpath(checkNil && !cls)) return nil;
        if (fastpath(!cls->ISA()->hasCustomAWZ())) {
            return _objc_rootAllocWithZone(cls, nil);
        }
    #endif
    
        // No shortcuts available.
        if (allocWithZone) {
            return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
        }
        return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
    }
    

    当调用alloc方法时,我们可以通过底层调用清楚的看到Objc4采用Runtime机制发送了信息给了allocWithZone方法。此时,allocWithZone接收到Runtime发送的消息,被调用[cls allocWithZone:nil]。

    + (id)allocWithZone:(struct _NSZone *)zone {
        return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
    }
    
    NEVER_INLINE
    id
    _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
    {
        // allocWithZone under __OBJC2__ ignores the zone parameter
        return _class_createInstanceFromZone(cls, 0, nil,
                                             OBJECT_CONSTRUCT_CALL_BADALLOC);
    }
    
    static ALWAYS_INLINE id
    _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
                                  int construct_flags = OBJECT_CONSTRUCT_NONE,
                                  bool cxxConstruct = true,
                                  size_t *outAllocatedSize = nil)
    {
        ASSERT(cls->isRealized());
    
        // Read class's info bits all at once for performance
        bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
        bool hasCxxDtor = cls->hasCxxDtor();
        bool fast = cls->canAllocNonpointer();
        size_t size;
    
        size = cls->instanceSize(extraBytes);
        if (outAllocatedSize) *outAllocatedSize = size;
    
        id obj;
        if (zone) {
            obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
        } else {
            obj = (id)calloc(1, size);
        }
        if (slowpath(!obj)) {
            if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
                return _objc_callBadAllocHandler(cls);
            }
            return nil;
        }
    
        if (!zone && fast) {
            obj->initInstanceIsa(cls, hasCxxDtor);
        } else {
            // Use raw pointer isa on the assumption that they might be
            // doing something weird with the zone or RR.
            obj->initIsa(cls);
        }
    
        if (fastpath(!hasCxxCtor)) {
            return obj;
        }
    
        construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
        return object_cxxConstructFromClass(obj, cls, construct_flags);
    }
    

    在调用方法中,我们可以看到_class_createInstanceFromZone()丢弃了zone参数,开发者即使自定义allocWithZone方法也无法指定想使用的zone,对象zone实际上由libmolloc库分配。
    初始化流程:

    1. size = cls->instanceSize(extraBytes)初始化对象所需要的内存大小;
    2. 通过obj = (id)calloc(1, size)生成一份内存大小,malloc 不会设置内存为零,而 calloc 会设置分配的内存为零;
    3. 调用obj->initInstanceIsa(cls, hasCxxDtor),初始化isa与cls类进行关联;
    4. 返回对象;

    调用init方式

    调用[[NSObject alloc] init] 方法会返回allocWithZone:生成的obj对象。

    - (id)init {
        return _objc_rootInit(self);
    }
    

    调用new方式

    [NSObject new]调用实际上自身调用了[[NSObject alloc] init],在默认init方式下可以直接调用new方法。

    + (id)new {
        return [callAlloc(self, false/*checkNil*/) init];
    }
    

    销毁

    调用dealloc

    - (void)dealloc {
        _objc_rootDealloc(self);
    }
    
    void
    _objc_rootDealloc(id obj)
    {
        ASSERT(obj);
    
        obj->rootDealloc();
    }
    
    inline void
    objc_object::rootDealloc()
    {
        if (isTaggedPointer()) return;  // fixme necessary?
    
        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);
        }
    }
    

    _objc_isTaggedPointer判断是否标记当前指针为存储64bit小对象指针。

    static inline bool 
    _objc_isTaggedPointer(const void * _Nullable ptr)
    {
        return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK; // 标记位标记该指针是否是tagged pointer
    }
    

    Tagged Pointer是苹果在64bit设备提出的一种存储小对象的技术,它具有以下特点:

    1. Tagged Pointer指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象皮的普通变量而已。
    2. 它的内存并不存储在堆中,也不需要 malloc 和 free,不走引用计数那一套逻辑,由系统来处理释放。
    3. 在内存读取上有着 3 倍的效率,创建时比以前快 106 倍。
    4. 可以通过设置环境变量OBJC_DISABLE_TAGGED_POINTERS来有开发者决定是否使用这项技术。
    1. Tagged Pointer判断,当返回值为真时,则说明当前指针指的指是对象而不是地址,应该由系统来处理释放,直接返回return。
    2. 判断当前isa是否经过优化,当nonpointer为1表示当前的isa是经过优化的;
    3. 判断当前isa是否被弱引用或曾经被弱引用过,!isa.weakly_referenced会更快的释放;
    4. 判断当前isa是否有关联对象,!isa.has_assoc会更快的释放;
    5. 判断当前isa是否有C++析构函数函数,!isa.has_cxx_dtor会更快的释放;
    6. 判断当前isa是否有扩展的引用计数。当一个对象的引用计数比较少时,其引用计数就记录在isa中,当引用计数大于某个值时就会采用sideTable来协助存储引用计数,!isa.has_sidetable_rc会更快的释放;
      以上有一点不满足,则会调用以下方法进行释放操作。
    id 
    object_dispose(id obj)
    {
        if (!obj) return nil;
    
        objc_destructInstance(obj);    
        free(obj);
    
        return 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;
    }
    

    判断obj当前对象持有C++的析构函数和关联对象,并释放cxx与assoc。
    当前判断obj是否是经过isa优化的,当slowpath(!isa.nonpointer)为真时,说明isa经过优化,通过SlideTable优化。从SlideTable表中移除。

    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());
    }
    
    void 
    objc_object::sidetable_clearDeallocating()
    {
        SideTable& table = SideTables()[this];
    
        // clear any weak table items
        // clear extra retain count and deallocating bit
        // (fixme warn or abort if extra retain count == 0 ?)
        table.lock();
        RefcountMap::iterator it = table.refcnts.find(this);
        if (it != table.refcnts.end()) {
            if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
                weak_clear_no_lock(&table.weak_table, (id)this);
            }
            table.refcnts.erase(it);
        }
        table.unlock();
    }
    
    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();
    }
    

    完成以上操作后,进行free(obj)释放对象。

    相关文章

      网友评论

        本文标题:NSObject 底层原理分析(一)

        本文链接:https://www.haomeiwen.com/subject/syewlrtx.html