iOS:对象释放流程

参考文档:

  • 对象销毁过程:
    Objc对象的销毁过程
    iOS引用计数

对象生命周期由引用计数器的概念管理,每次对象失去一个被引用关系,会调用对应的release操作做引用计数器减1操作,当对象的引用计数为0时,会走真正的对象释放流程,走到dealloc释放方法;

引用计数器-对象调用release

objc_object::release()
{
    ASSERT(!isTaggedPointer());

    if (fastpath(!ISA()->hasCustomRR())) {
        sidetable_release();
        return;
    }

    ((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(release));
}
bjc_object::sidetable_release(bool performDealloc)
{
#if SUPPORT_NONPOINTER_ISA
    assert(!isa.nonpointer);
#endif
    SideTable& table = SideTables()[this];

    bool do_dealloc = false;
    //加锁
    table.lock();
//获取当前对象所在的sidetable(一个hash表),在sidetable.refcnts(RefcountMap,一个map)中查到当前对象的迭代器
    RefcountMap::iterator it = table.refcnts.find(this);
    //接着判断迭代器是否是指向了sidetable的end
    //如果是就代表找不到:
    if (it == table.refcnts.end()) {
      //将对象标记为“正在析构”
      //标记需要dealloc
        do_dealloc = true;
        table.refcnts[this] = SIDE_TABLE_DEALLOCATING;
    } else if (it->second < SIDE_TABLE_DEALLOCATING) {
      //判断之前存储的引用计数值是否为 0,避免负数
      //将对象标记为“正在析构”
      //标记需要dealloc
        // SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
        do_dealloc = true;
        it->second |= SIDE_TABLE_DEALLOCATING;
    } else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
       //将引用计数减一
        it->second -= SIDE_TABLE_RC_ONE;
    }
   //解锁
    table.unlock();
    if (do_dealloc  &&  performDealloc) {
    //调用对象的dealloc方法:
        ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
    }
    return do_dealloc;
}

dealloc调用流程

NSObject的dealloc会调用 _objc_rootDealloc(self);

- (void)dealloc {
    _objc_rootDealloc(self);
}
_objc_rootDealloc(id obj)
{
    //是否还活着 断言
    assert(obj);
   //调用 objc_object::rootDealloc()
    obj->rootDealloc();
}
objc_object::rootDealloc()
{
    //是否使用TaggedPointer优化 是直接返回
    if (isTaggedPointer()) return;  // fixme necessary?

   //不需要处理object_dispose的所有内容
    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
        object_dispose((id)this);
    }
}
id object_dispose(id obj)
{
    //是否为nil 直接返回
    if (!obj) return nil;
    //调用 void *objc_destructInstance(id obj)
    objc_destructInstance(obj);    
    free(obj);
    return nil;
}
void *objc_destructInstance(id obj) 
{
//如果不为nil 才处理
    if (obj) {
        //是否有析构函数  这个bool值取决于当前类以及父类往上是否有实例变量,如果有实例变量当前类就有.cxxDestruct,当前类或父类有此方法值=YES,都没有才=NO
        bool cxx = obj->hasCxxDtor();
       //是否有关联对象
        bool assoc = obj->hasAssociatedObjects();
        // This order is important.
      //如果有析构函数 调用 void object_cxxDestruct(id obj)
        if (cxx) object_cxxDestruct(obj);
       //如果有关联对象 移除关联对象
        if (assoc) _object_remove_assocations(obj);
        //调用objc_clear_deallocating()清空引用计数表
        obj->clearDeallocating();
    }
    return obj;
}
析构函数
void object_cxxDestruct(id obj)
{
   //如果为nil 直接retun
    if (!obj) return;
   //是否使用TaggedPointer优化 是直接return
    if (obj->isTaggedPointer()) return;
   //调用 static void object_cxxDestructFromClass(id obj, Class cls)
    object_cxxDestructFromClass(obj, obj->ISA());
}
static void object_cxxDestructFromClass(id obj, Class cls)
{
    void (*dtor)(id);

    // Call cls's dtor first, then superclasses's dtors.

    //往父类递归调用.cxxDestruct 直到hasCxxDtor=NO return结束
    for ( ; cls; cls = cls->superclass) {
        if (!cls->hasCxxDtor()) return; 
        // 在类和缓存中查找方法
        dtor = (void(*)(id))
            lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);
        // 查找到cxx_destruct方法(SEL_cxx_destruct = sel_registerNameNoLock(".cxx_destruct", NO);)
        if (dtor != (void(*)(id))_objc_msgForward_impcache) {
            if (PrintCxxCtors) {
                _objc_inform("CXX: calling C++ destructors for class %s", 
                             cls->nameForLogging());
            }
            (*dtor)(obj);
        }
    }
}

.cxx_destruct方法原本是为了C++对象析构的,ARC借用了这个方法插入代码实现了自动内存释放的工作

关联对象
void objc_removeAssociatedObjects(id object) 
{
    if (object && object->hasAssociatedObjects()) {
        _object_remove_assocations(object);
    }
}

关联对象由一个单独单独的管理类进行统一管理,维护有一个哈希表AssociationsHashMap,key为obj,value为一个哈希表ObjectAssociationMap,存储着关联的属性:值ObjcAssociation;可以理解为两层哈希表;
{
obj:{
name:value,
age:value
}
}

void _object_remove_assocations(id object) {
    vector< ObjcAssociation,ObjcAllocator > elements;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        if (associations.size() == 0) return;
        disguised_ptr_t disguised_object = DISGUISE(object);
        AssociationsHashMap::iterator i = associations.find(disguised_object);
        if (i != associations.end()) {
            // copy all of the associations that need to be removed.
            ObjectAssociationMap *refs = i->second;
            for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
                elements.push_back(j->second);
            }
            // remove the secondary table.
            delete refs;
            associations.erase(i);
        }
    }
    // the calls to releaseValue() happen outside of the lock.
    for_each(elements.begin(), elements.end(), ReleaseValue());
}
struct ReleaseValue {
    void operator() (ObjcAssociation &association) {
        releaseValue(association.value(), association.policy());
    }
};
static void releaseValue(id value, uintptr_t policy) {
    if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {
        return objc_release(value);
    }
}
清空引用计数表
void 
objc_clear_deallocating(id obj) 
{
    assert(obj);

    if (obj->isTaggedPointer()) return;
    obj->clearDeallocating();
}
objc_object::clearDeallocating()
{
    if (slowpath(!isa.nonpointer)) {
        // Slow path for raw pointer isa.
       //调用sidetable_clearDeallocating()把对象的weak指针置nil,把对象的计数引用移除
        sidetable_clearDeallocating();
    }
    else if (slowpath(isa.weakly_referenced  ||  isa.has_sidetable_rc)) {
        /判断是否有过弱引用   是否因为计数太大有多个sidetable
        // Slow path for non-pointer isa with weak refs and/or side table data.
       //调用clearDeallocating_slow();内部再分开判断各自实现sidetable_clearDeallocating的内容
        clearDeallocating_slow();
    }

    assert(!sidetable_present());
}
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指针置nil,
            weak_clear_no_lock(&table.weak_table, (id)this);
        }
        // 把对象的计数引用移除
        table.refcnts.erase(it);
    }
    table.unlock();
}


部分关键点注解

Tagged Pointer介绍

从64bit开始,iOS引入Tagged Pointer技术,用于优化NSNumber、NSDate、NSString等小对象存储。指针并没有指向真正的内容存储在指针中;这类对象没有引用计数器内存管理流程。

#   define _OBJC_TAG_MASK (1UL<<63)
_objc_isTaggedPointer(const void * _Nullable ptr)
{
    return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}

nonpointer介绍

在支持nonpointer的对象中引用计数会又选存储在extra_rc中,isa位域extra_rc溢出,则会选择将将引用计数的RC_HALF(如果extra_rc占据8bit,则RC_HALF=2^7)保存在isa中,另一半RC_HALF叠加保存在sidetable中.之所以这样选择是因为isa_t的存取理论上会比sidetable的操作效率上快很多,这样做既可以使超出extra_rc存储范围的引用计数得到有效存储,又可以确保引用计数的增减足够快速(存取都以extra_rc优先)。

1、在开启nonpointer的对象中,对象的引用计数包括两部分:
1)存储在isa中的引用计数(isa.extra_rc);
2)存储在sidetable中的引用计数.
2、在未开启nonpointer的对象中,对象的引用计数全部存储在sidetable中,只需要从sidetable中获取就可以.

isa_t结构(arm64)


# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
#   define ISA_BITFIELD                                                      \
      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 deallocating      : 1;                                       \
      uintptr_t has_sidetable_rc  : 1;                                       \
      uintptr_t extra_rc          : 19
#   define RC_ONE   (1ULL<<45)
#   define RC_HALF  (1ULL<<18)

fastpath、slowpath

#define fastpath(x) (__builtin_expect(bool(x), 1))
#define slowpath(x) (__builtin_expect(bool(x), 0))

__builtin_expect指令的写法为:__builtin_expect(EXP, N)。
意思是:EXP==N的概率很大。

__builtin_expect() 是 GCC (version >= 2.96)提供给程序员使用的,目的是将“分支转移”的信息提供给编译器,这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。
__builtin_expect((x),1)表示 x 的值为真的可能性更大;
__builtin_expect((x),0)表示 x 的值为假的可能性更大。
也就是说,使用fastpath(),执行 if 后面的语句的机会更大,使用 slowpath(),执行 else 后面的语句的机会更大。通过这种方式,编译器在编译过程中,会将可能性更大的代码紧跟着前面的代码,从而减少指令跳转带来的性能上的下降。

.cxx_desctruct

参考链接:

  • 越狱研究点滴---ARC下dealloc过程及.cxx_destruct的探究

ARC下对象的成员变量于编译器插入的.cxx_desctruct方法自动释放;
ARC下[super dealloc]方法也由编译器自动插入;

你可能感兴趣的:(iOS:对象释放流程)