Skip to content

Latest commit

 

History

History
499 lines (396 loc) · 14.7 KB

2021-4-9 对象的初始化以及释放.md

File metadata and controls

499 lines (396 loc) · 14.7 KB

对象的初始化以及释放

你知道 alloc init new release 这些方法都干了什么吗?

对象创建

一个简单的对象创建方式为 [[NSObject alloc] init] 。其中 allocinit 都做了什么呢? 或者说 [NSObject new]

alloc:

先写结论,做了三件事:

  • 分配内存空间,生成实例对象
  • 初始化实例对象isa
  • 判断实例对象是否存在c++析构函数,如果有,则调用

开始看代码,找到 NSObject.mm文件


+ (id)alloc {
    return _objc_rootAlloc(self);
}

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__   objc 2.0版本

// #define LLVM_LIKELY fastpath  代表大概率会走这个方法
// #define LLVM_UNLIKELY slowpath 代表大概率不走这个方法

    if (slowpath(checkNil && !cls)) return nil;
    if (fastpath(!cls->ISA()->hasCustomAWZ())) {
        return _objc_rootAllocWithZone(cls, nil);
    }
#endif
    // 这部分省略
    if (allocWithZone) {
        return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
    }
    return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}

_objc_rootAllocWithZone 这个方法做了什么呢?(此时取的函数为的是objc-runtime-new.mm (objc-runtime-old.mm 不考虑))
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;
    // 动态分配内存空间生成实例(都是c++内存分配的方法)
    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;
    }

//  下面两个初始化 Isa初始化 都最终会调用objc_object::initIsa方法 在这就不展示initIsa方法
    if (!zone && fast) {
      //initIsa(cls, true, hasCxxDtor);
        obj->initInstanceIsa(cls, hasCxxDtor);
    } else {
      // initIsa(cls, false, false);
        obj->initIsa(cls);
    }

    if (fastpath(!hasCxxCtor)) {
        return obj;
    }

    construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;

    // Recursively call C++ constructors on obj, starting with base class's
    // ctor method (if any) followed by subclasses' ctors (if any), stopping    at cls's ctor (if any).

    // 翻译一下: 递归调用obj上的c++构造函数,从基类的ctor方法(如果有的话)开始,然后是子类的ctor方法(如果有的话),直到cls的ctor为止(如果有的话)。
    // object_cxxConstructFromClass 就不详细展示了
    return object_cxxConstructFromClass(obj, cls, construct_flags);
}




拓展: isa_t 为一个联合体,所有信息公用一块内存,起到节省内存的作用。

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_BITFIELD 部分,方便下面release方法的理解,引用 iOS概念攻坚之路(三):内存管理 中的 NONPOINTER_ISA 部分


ISA_BITFIELD 部分
// 以x86_64 架构 为例子

    uintptr_t nonpointer        : 1;  // 0:普通指针,指向class,1:优化过,使用位域存储更多信息
    uintptr_t has_assoc         : 1;  // 对象是否含有或曾经含有关联引用
    uintptr_t has_cxx_dtor      : 1;  // 表示是否有C++析构函数或OC的dealloc
    uintptr_t shiftcls          : 44; // 存放着 Class、Meta-Class 对象的内存地址信息
    uintptr_t magic             : 6;  // 用于在调试时分辨对象是否未完成初始化
    uintptr_t weakly_referenced : 1;  // 是否被弱引用指向
    uintptr_t deallocating      : 1;  // 对象是否正在释放
    uintptr_t has_sidetable_rc  : 1;  // 是否需要使用 sidetable 来存储引用计数
    uintptr_t extra_rc          : 8;  // 引用计数能够用 8 个二进制位存储时,直接存储在这里

init: 从代码上看是返回了当前的对象

NSObject.mm 文件:
- (id)init {
    return _objc_rootInit(self);
}

id
_objc_rootInit(id obj)
{
    // In practice, it will be hard to rely on this function.
    // Many classes do not properly chain -init calls.
    return obj;
}

【注】意味着对于NSObject来说,多次调用init方法还是返回本身。

new:
相当于 [[NSObject alloc] init]]

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

看了源码来让我们验证一个事情,[NSObject alloc][[NSObject alloc] init] 是否是同一个对象,如图

图

果然是同个对象,但是在实际写代码的时候 不建议直接写 [Class alloc] 的方式。

顺便补充一下在类方法中的self和实例方法中的self的区别

为啥都是self,第一眼认为是同一个self

+ (id)alloc {
   return _objc_rootAlloc(self);
}
- (id)init {
   return _objc_rootInit(self);
}

其实在类方法中的self代表的是class,对象方法中的self代表的是当前对象。

测试一下

+(void)testClassFunc {
    self;
}

-(void)testInstanceFunc {
    self;
    // [self class];  等价于 +(void)testClassFunc中的self
}

通过clang编译之后的cpp文件找到对应的方法,从传参就能看出两者的区别。
我们可以看到在类方法中self的类型为Class,示例对象方法中类型为对象。

static void _C_Test_testClassFunc(Class self, SEL _cmd) {
    self;
}
static void _I_Test_testInstanceFunc(Test * self, SEL _cmd) {
    self;
}

对象释放

我们都知道object对象释放的时候,在mrc中是通过release方法去释放对象,那release的时候具体做了些什么呢?


- (oneway void)release {
    _objc_rootRelease(self);
}


NEVER_INLINE void
_objc_rootRelease(id obj)
{
    ASSERT(obj);

    obj->rootRelease();
}

ALWAYS_INLINE bool
objc_object::rootRelease()
{
    return rootRelease(true, false);
}

ALWAYS_INLINE bool
objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
{
    if (isTaggedPointer()) return false;

    bool sideTableLocked = false;

    isa_t oldisa;
    isa_t newisa;

 retry:
    do {
        oldisa = LoadExclusive(&isa.bits);
        newisa = oldisa;
        // 当前 nonpointer 都为1 所有这个判断里面的方法不会执行
        if (slowpath(!newisa.nonpointer)) {
            ClearExclusive(&isa.bits);
            if (rawISA()->isMetaClass()) return false;
            if (sideTableLocked) sidetable_unlock();
            return sidetable_release(performDealloc);
        }
        // don't check newisa.fast_rr; we already called any RR overrides
        uintptr_t carry;
        newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc--
        if (slowpath(carry)) {
            // don't ClearExclusive()
            goto underflow;
        }
    } while (slowpath(!StoreReleaseExclusive(&isa.bits,
                                             oldisa.bits, newisa.bits)));

    if (slowpath(sideTableLocked)) sidetable_unlock();
    return false;

 underflow:
    // newisa.extra_rc-- underflowed: borrow from side table or deallocate

    // abandon newisa to undo the decrement
    newisa = oldisa;

    if (slowpath(newisa.has_sidetable_rc)) {
        if (!handleUnderflow) {
            ClearExclusive(&isa.bits);
            return rootRelease_underflow(performDealloc);
        }

        // Transfer retain count from side table to inline storage.

        if (!sideTableLocked) {
            ClearExclusive(&isa.bits);
            sidetable_lock();
            sideTableLocked = true;
            // Need to start over to avoid a race against
            // the nonpointer -> raw pointer transition.
            goto retry;
        }

        // Try to remove some retain counts from the side table.        
        size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);

        // To avoid races, has_sidetable_rc must remain set
        // even if the side table count is now zero.

        if (borrowed > 0) {
            // Side table retain count decreased.
            // Try to add them to the inline count.
            newisa.extra_rc = borrowed - 1;  // redo the original decrement too
            bool stored = StoreReleaseExclusive(&isa.bits,
                                                oldisa.bits, newisa.bits);
            if (!stored) {
                // Inline update failed.
                // Try it again right now. This prevents livelock on LL/SC
                // architectures where the side table access itself may have
                // dropped the reservation.
                isa_t oldisa2 = LoadExclusive(&isa.bits);
                isa_t newisa2 = oldisa2;
                if (newisa2.nonpointer) {
                    uintptr_t overflow;
                    newisa2.bits =
                        addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);
                    if (!overflow) {
                        stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits,
                                                       newisa2.bits);
                    }
                }
            }

            if (!stored) {
                // Inline update failed.
                // Put the retains back in the side table.
                sidetable_addExtraRC_nolock(borrowed);
                goto retry;
            }

            // Decrement successful after borrowing from side table.
            // This decrement cannot be the deallocating decrement - the side
            // table lock and has_sidetable_rc bit ensure that if everyone
            // else tried to -release while we worked, the last one would block.
            sidetable_unlock();
            return false;
        }
        else {
            // Side table is empty after all. Fall-through to the dealloc path.
        }
    }

    // Really deallocate.

    if (slowpath(newisa.deallocating)) {
        ClearExclusive(&isa.bits);
        if (sideTableLocked) sidetable_unlock();
        return overrelease_error();
        // does not actually return
    }
    newisa.deallocating = true;
    if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;

    if (slowpath(sideTableLocked)) sidetable_unlock();

    __c11_atomic_thread_fence(__ATOMIC_ACQUIRE);

    if (performDealloc) {
        ((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
    }
    return true;
}

上述的 objc_object::rootRelease 较为复杂,解释一下大概逻辑为不断的进行引用计数-1,然后去释放对应的sidetable,最后会调用对象的 dealloc 方法

我们来看看 dealloc干了什么

- (void)dealloc {
    _objc_rootDealloc(self);
}

inline void
objc_object::rootDealloc()
{
    if (isTaggedPointer()) return;

    // #define LLVM_LIKELY fastpath  代表大概率会走这个方法

    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);
    }
}

从上面的方法看,大概可以知道如果这个object没有 Associated Objectsweak对象, c++的destructor(析构函数)引用计数为0 会直接调用 free 函数。(意味着会清除 Associated Objects和weak对象

那我们再来看 object_dispose函数:

/***********************************************************************
* object_dispose
* fixme
* Locking: none
**********************************************************************/
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());
}


// Slow path of clearDeallocating()
// for objects with nonpointer isa
// that were ever weakly referenced
// or whose retain count ever overflowed to the side table.
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
    ASSERT(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc));

    SideTable& table = SideTables()[this];
    table.lock();
    // 移除weak
    if (isa.weakly_referenced) {
        weak_clear_no_lock(&table.weak_table, (id)this);
    }
    if (isa.has_sidetable_rc) {
        table.refcnts.erase(this);
    }
    table.unlock();
}

当对象被移除的时候,我们不需要手动的去释放Assocation对象和weak对象。

源码参考 objc4-781