阅读 184

iOS 性能优化(引用计数&弱引用)

前言

这篇文章会通过源码去介绍引用计数和weak的实现,上一篇文章初略介绍了一下

weak底层原理

weak的创建

使用weak修饰符有两种情况

  • property中使用weak修饰
@property (nonatomic,weak) NSObject *referent;
复制代码
// 底层实现函数入口
id objc_storeWeak(id *location, id newObj)
{
    return storeWeak<DoHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object *)newObj);
}
复制代码
  • 使用__weak修饰对象
NSObject *obj1 = [[NSObject alloc] init];
id __weak obj2 = obj1;
复制代码
// 底层实现函数入口
id objc_initWeak(id *location, id newObj)
{
    if (!newObj) {
        *location = nil;
        return nil;
    }

    return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object*)newObj);
}
复制代码

可以看出,底层都是调用storeWeak方法,区别在于模板的第一个参数haveOld

进入storeWeak函数

template <HaveOld haveOld, HaveNew haveNew,
          CrashIfDeallocating crashIfDeallocating>
static id 
storeWeak(id *location, objc_object *newObj)
{
    assert(haveOld  ||  haveNew);
    if (!haveNew) assert(newObj == nil);

    Class previouslyInitializedClass = nil;
    id oldObj;
    SideTable *oldTable;
    SideTable *newTable;

    // Acquire locks for old and new values.
    // Order by lock address to prevent lock ordering problems. 
    // Retry if the old value changes underneath us.
 retry:
    // 如果weak指针之前弱引用过一个obj,则将这个obj所对应的SideTable取出,赋值给oldTable
    if (haveOld) {
        oldObj = *location;
        oldTable = &SideTables()[oldObj];
    } else {
        // 没有弱引用过,则oldTable = nil
        oldTable = nil;
    }
    // 如果weak指针要弱引用一个新的obj,则将该obj对应的SideTable取出,赋值给newTable
    if (haveNew) {
        newTable = &SideTables()[newObj];
    } else {
        newTable = nil;
    }
    // 加锁操作,防止多线程中竞争冲突
    SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
    // location 应该与 oldObj 保持一致,如果不同,说明当前的 location 已经处理过 oldObj 可是又被其他线程所修改
    if (haveOld  &&  *location != oldObj) {
        SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
        goto retry;
    }

    // Prevent a deadlock between the weak reference machinery
    // and the +initialize machinery by ensuring that no 
    // weakly-referenced object has an un-+initialized isa.
    if (haveNew  &&  newObj) {
        Class cls = newObj->getIsa();
        // 如果cls还没有初始化,先初始化,再尝试设置弱引用
        if (cls != previouslyInitializedClass  &&  
            !((objc_class *)cls)->isInitialized()) 
        {
            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
            _class_initialize(_class_getNonMetaClass(cls, (id)newObj));
            
            // 完成初始化后进行标记
            previouslyInitializedClass = cls;
            // newObj 初始化后,重新获取一遍newObj
            goto retry;
        }
    }

    // Clean up old value, if any.
    // 如果weak指针之前弱引用过别的对象oldObj,则调用weak_unregister_no_lock,在oldObj的weak_entry_t中移除该weak指针地址
    if (haveOld) {
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    }

    // Assign new value, if any.
    // 如果weak指针需要弱引用新的对象newObj
    if (haveNew) {
        // 调用weak_register_no_lock方法,将weak指针的地址记录到newObj对应的weak_entry_t中
        newObj = (objc_object *)
            weak_register_no_lock(&newTable->weak_table, (id)newObj, location, 
                                  crashIfDeallocating);
        // weak_register_no_lock returns nil if weak store should be rejected

        // Set is-weakly-referenced bit in refcount table.
        // 更新newObj的isa指针的weakly_referenced bit标志位
        if (newObj  &&  !newObj->isTaggedPointer()) {
            newObj->setWeaklyReferenced_nolock();
        }

        // Do not set *location anywhere else. That would introduce a race.
        // *location 赋值,也就是将weak指针直接指向了newObj,而且没有将newObj的引用计数+1
        *location = (id)newObj;
    }
    else {
        // No new value. The storage is not changed.
    }
    
    SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);

    return (id)newObj;
}
复制代码

因为我们这里是第一次调用,所以是一个新的对象,也就是haveNew的情况,获取到的是新的散列表SideTable,主要执行了weak_register_no_lock方法来进行插入。接着我们来分析weak_register_no_lock函数,是怎么注册弱引用的。

id 
weak_register_no_lock(weak_table_t *weak_table, id referent_id, 
                      id *referrer_id, bool crashIfDeallocating)
{
    // 被弱引用的对象
    objc_object *referent = (objc_object *)referent_id;
    // 指向弱引用对象的指针
    objc_object **referrer = (objc_object **)referrer_id;
    // 如果被弱引用对象referent为nil 或者被弱引用对象采用了TaggedPointer计数方式,则直接返回
    if (!referent  ||  referent->isTaggedPointer()) return referent_id;

    // ensure that the referenced object is viable
    // 确保被引用的对象可用(没有在析构,同时应该支持weak弱引用)
    bool deallocating;
    if (!referent->ISA()->hasCustomRR()) {
        deallocating = referent->rootIsDeallocating();
    }
    else {
        BOOL (*allowsWeakReference)(objc_object *, SEL) = 
            (BOOL(*)(objc_object *, SEL))
            object_getMethodImplementation((id)referent, 
                                           SEL_allowsWeakReference);
        if ((IMP)allowsWeakReference == _objc_msgForward) {
            return nil;
        }
        deallocating =
            ! (*allowsWeakReference)(referent, SEL_allowsWeakReference);
    }
    // 如果是正在析构的对象,那么不能够被弱引用
    if (deallocating) {
        if (crashIfDeallocating) {
            _objc_fatal("Cannot form weak reference to instance (%p) of "
                        "class %s. It is possible that this object was "
                        "over-released, or is in the process of deallocation.",
                        (void*)referent, object_getClassName((id)referent));
        } else {
            return nil;
        }
    }

    // now remember it and where it is being stored
    // 在 weak_table 中找到被弱引用对象 referent 对应的 weak_entry,并将 referrer 加入到 weak_entry 中
    weak_entry_t *entry;
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        // 如果能找到 weak_entry,则将 referrer 追加到 weak_entry 中
        append_referrer(entry, referrer);
    } 
    else {
        // 如果找不到 weak_entry,就新建一个
        weak_entry_t new_entry(referent, referrer);
        // 判断weak_table的容量是否足够
        weak_grow_maybe(weak_table);
        // 把新建的entry插入到weak_table
        weak_entry_insert(weak_table, &new_entry);
    }

    // Do not set *referrer. objc_storeWeak() requires that the 
    // value not change.

    return referent_id;
}
复制代码

被弱引用对象所在的weak_table中的weak_entry_t哈希数组中取出对应的weak_entry_t,如果weak_entry_t不存在,则会新建一个。然后将指向被弱引用对象地址的指针referrer通过函数append_referrer插入到对应的weak_entry_t引用数组。

static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
    // 如果weak_entry 使用静态数组 inline_referrers
    if (! entry->out_of_line()) {
        // Try to insert inline.
        // 尝试将 referrer 插入数组
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            if (entry->inline_referrers[i] == nil) {
                entry->inline_referrers[i] = new_referrer;
                return;
            }
        }

        // 如果inline_referrers的位置已经存满了,则要转型为 referrers,动态数组
        weak_referrer_t *new_referrers = (weak_referrer_t *)
            calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
        // This constructed table is invalid, but grow_refs_and_insert
        // will fix it and rehash it.
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            new_referrers[i] = entry->inline_referrers[i];
        }
        entry->referrers = new_referrers;
        entry->num_refs = WEAK_INLINE_COUNT;
        entry->out_of_line_ness = REFERRERS_OUT_OF_LINE;
        entry->mask = WEAK_INLINE_COUNT-1;
        entry->max_hash_displacement = 0;
    }

    assert(entry->out_of_line());
    // 如果动态数组中元素个数大于或等于数组总空间的3/4,则扩展数组空间为当前长度的一倍,然后将 referrer 插入数组
    if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
        return grow_refs_and_insert(entry, new_referrer);
    }
    // 如果不需要扩容,直接插入到weak_entry中
    size_t begin = w_hash_pointer(new_referrer) & (entry->mask);
    size_t index = begin;
    size_t hash_displacement = 0;
    while (entry->referrers[index] != nil) {
        hash_displacement++;
        index = (index+1) & entry->mask;
        if (index == begin) bad_weak_table(entry);
    }
    if (hash_displacement > entry->max_hash_displacement) {
        entry->max_hash_displacement = hash_displacement;
    }
    weak_referrer_t &ref = entry->referrers[index];
    ref = new_referrer;
    entry->num_refs++;
}
复制代码

不过现在的情况是新建的,所以我们关注另外一种情况

weak_entry_t new_entry(referent, referrer);
weak_grow_maybe(weak_table);
weak_entry_insert(weak_table, &new_entry);
复制代码
static void weak_grow_maybe(weak_table_t *weak_table)
{
    size_t old_size = TABLE_SIZE(weak_table);

    // Grow if at least 3/4 full.
    if (weak_table->num_entries >= old_size * 3 / 4) {
        weak_resize(weak_table, old_size ? old_size*2 : 64);
    }
}

static void weak_resize(weak_table_t *weak_table, size_t new_size)
{
    size_t old_size = TABLE_SIZE(weak_table);

    weak_entry_t *old_entries = weak_table->weak_entries;
    weak_entry_t *new_entries = (weak_entry_t *)
        calloc(new_size, sizeof(weak_entry_t));

    weak_table->mask = new_size - 1;
    weak_table->weak_entries = new_entries;
    weak_table->max_hash_displacement = 0;
    weak_table->num_entries = 0;  // restored by weak_entry_insert below
    // 重新将老的数据插入到插入到新分配的空间中
    if (old_entries) {
        weak_entry_t *entry;
        weak_entry_t *end = old_entries + old_size;
        for (entry = old_entries; entry < end; entry++) {
            if (entry->referent) {
                weak_entry_insert(weak_table, entry);
            }
        }
        free(old_entries);
    }
}
复制代码

weak_table的num_entries大于总量的3/4,则扩容为原来的两倍大小的2倍。
接下来插入这个新的weak_entry_t

static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry)
{
    weak_entry_t *weak_entries = weak_table->weak_entries;
    assert(weak_entries != nil);

    size_t begin = hash_pointer(new_entry->referent) & (weak_table->mask);
    size_t index = begin;
    size_t hash_displacement = 0;
    while (weak_entries[index].referent != nil) {
        index = (index+1) & weak_table->mask;
        if (index == begin) bad_weak_table(weak_entries);
        hash_displacement++;
    }

    weak_entries[index] = *new_entry;
    weak_table->num_entries++;

    if (hash_displacement > weak_table->max_hash_displacement) {
        weak_table->max_hash_displacement = hash_displacement;
    }
}
复制代码

weak的销毁

销毁的过程是调用析构函数(dealloc),下面就来分析析构函数的原理

- (void)dealloc {
    _objc_rootDealloc(self);
}
// _objc_rootDealloc
void
_objc_rootDealloc(id obj)
{
    // 断言,判断obj是否为空
    assert(obj);

    obj->rootDealloc();
}
复制代码

进入rootDealloc内部函数

inline void
objc_object::rootDealloc()
{
    // 如果是TaggedPointer类型就直接退出
    if (isTaggedPointer()) return;  // fixme necessary?

    if (fastpath(isa.nonpointer  &&             //对象是否采用了优化的isa计数方式
                 !isa.weakly_referenced  &&     //对象没有被弱引用
                 !isa.has_assoc  &&             //对象没有关联对象
                 !isa.has_cxx_dtor  &&          //对象没有自定义的C++析构函数
                 !isa.has_sidetable_rc))        //对象没有用到sideTable来做引用计数
    {
        assert(!sidetable_present());
        // 如果以上判断都符合条件,就会调用C函数 free 将对象释放
        free(this);
    } 
    else {
        // 如果以上判断没有通过
        object_dispose((id)this);
    }
}
复制代码

继续往下走

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.
        // 如果有C++析构函数,则从类中销毁C++析构函数
        if (cxx) object_cxxDestruct(obj);
        // 如果有关联对象,则移除所有的关联对象,并将其自身从Association Manager的map中移除
        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.
        // 如果要释放的对象没有采用了优化过的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.
        // 如果要释放的对象采用了优化过的isa引用计数,并且有弱引用或者使用了sideTable的辅助引用计数
        clearDeallocating_slow();
    }

    assert(!sidetable_present());
}
复制代码

这里分两种情况

  • 释放的对象没有采用了优化过的isa引用计数
void 
objc_object::sidetable_clearDeallocating()
{
    // 在全局的SideTables中,获取当前对象对应的sidetable
    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();
    // 在散列表SideTable中找到对应的引用计数表RefcountMap,拿到要释放的对象的引用计数
    RefcountMap::iterator it = table.refcnts.find(this);
    if (it != table.refcnts.end()) {
        // 如果要释放的对象被弱引用了,通过weak_clear_no_lock函数将指向该对象的弱引用指针置为nil
        if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
            weak_clear_no_lock(&table.weak_table, (id)this);
        }
        // 从引用计数表中擦除该对象的引用计数
        table.refcnts.erase(it);
    }
    table.unlock();
}
复制代码
  • 释放的对象采用了优化过的isa引用计数
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
    assert(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc));
    // 在全局的SideTables中,获取当前对象对应的sidetable
    SideTable& table = SideTables()[this];
    table.lock();
    if (isa.weakly_referenced) {
        //要释放的对象被弱引用了,通过weak_clear_no_lock函数将指向该对象的弱引用指针置为nil
        weak_clear_no_lock(&table.weak_table, (id)this);
    }
    //使用了sideTable的辅助引用计数,直接在SideTable中擦除该对象的引用计数
    if (isa.has_sidetable_rc) {
        table.refcnts.erase(this);
    }
    table.unlock();
}
复制代码

这两个方法中都有调用weak_clear_no_lock,这个函数的作用就是将指向被弱引用对象的弱引用指针置为nil

void 
weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
{
    //获取被弱引用对象的地址
    objc_object *referent = (objc_object *)referent_id;
    // 根据对象地址找到被弱引用对象referent在weak_table中对应的weak_entry_t
    weak_entry_t *entry = weak_entry_for_referent(weak_table, referent); 
    if (entry == nil) {
        //printf("XXX no entry for clear deallocating %p\n", referent);
        return;
    }

    // zero out references
    weak_referrer_t *referrers;
    size_t count;
    
    // 找出弱引用该对象的所有weak指针地址数组
    if (entry->out_of_line()) {
        referrers = entry->referrers;
        count = TABLE_SIZE(entry);
    } 
    else {
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }
    // 遍历取出每个weak指针的地址
    for (size_t i = 0; i < count; ++i) {
        objc_object **referrer = referrers[i]; 
        if (referrer) {
            // 如果weak指针确实弱引用了对象 referent,则将weak指针设置为nil
            if (*referrer == referent) { 
                *referrer = nil;
            }
            // 如果所存储的weak指针没有弱引用对象 referent,这可能是由于runtime代码的逻辑错误引起的,报错
            else if (*referrer) { 
                _objc_inform("__weak variable at %p holds %p instead of %p. "
                             "This is probably incorrect use of "
                             "objc_storeWeak() and objc_loadWeak(). "
                             "Break on objc_weak_error to debug.\n", 
                             referrer, (void*)*referrer, (void*)referent);
                objc_weak_error();
            }
        }
    }
    // 移除这个entry
    weak_entry_remove(weak_table, entry);
}
复制代码

dealloc方法进行释放的整个流程如下图所示:

引用计数

以下测试代码是基于MRC环境下测试的,设置MRC传送门

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NHText : NSObject

@end

NS_ASSUME_NONNULL_END
复制代码
#import "NHText.h"

@implementation NHText


- (void)dealloc{
    NSLog(@"NHText dealloc");
}

@end
复制代码
#import <Foundation/Foundation.h>
#import "NHText.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
    
        NHText *t = [NHText alloc];
        NSLog(@"retainCount = %lu",(unsigned long)[t retainCount]);
        [t retain];
        NSLog(@"retainCount = %lu",(unsigned long)[t retainCount]);
        [t release];
        NSLog(@"retainCount = %lu",(unsigned long)[t retainCount]);
        [t release];
        NSLog(@"retainCount = %lu",(unsigned long)[t retainCount]);
        
     }
    return 0;
}
复制代码

打印:

2020-02-17 11:58:11.020249+0800 NHText[1819:45917] retainCount = 1
2020-02-17 11:58:11.020368+0800 NHText[1819:45917] retainCount = 2
2020-02-17 11:58:11.020382+0800 NHText[1819:45917] retainCount = 1
2020-02-17 11:58:11.020391+0800 NHText[1819:45917] NHText dealloc
2020-02-17 11:58:11.020402+0800 NHText[1819:45917] retainCount = 1
Program ended with exit code: 0
复制代码

alloc

先看alloc方法里面的流程

+ (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 (slowpath(checkNil && !cls)) return nil;

#if __OBJC2__
    if (fastpath(!cls->ISA()->hasCustomAWZ())) { // 如果 cls 没有实现自定义 allocWithZone 方法
        if (fastpath(cls->canAllocFast())) { // 如果 cls 支持快速 Alloc
            bool dtor = cls->hasCxxDtor(); // 获取 cls 是否有自己的析构函数。
            id obj = (id)calloc(1, cls->bits.fastInstanceSize()); // 使用 calloc 根据 fastInstanceSize 的大小申请内存空间
            if (slowpath(!obj)) return callBadAllocHandler(cls); // 如果内存空间申请失败,调用 callBadAllocHandler
            obj->initInstanceIsa(cls, dtor); // 如果成功,初始化 isa
            return obj; // 返回对象
        }
        else { // 如果 cls 不支持快速 Alloc
            // Has ctor or raw isa or something. Use the slower path.
            id obj = class_createInstance(cls, 0); // 通过 class_createInstance 方法直接创建对象
            if (slowpath(!obj)) return callBadAllocHandler(cls); // 如果对象创建失败,调用 callBadAllocHandler
            return obj; // 返回对象
        }
    }
#endif

    // No shortcuts available.
    if (allocWithZone) return [cls allocWithZone:nil]; // 如果 allocWithZone 为 true,调用 allocWithZone 创建对象
    return [cls alloc]; // 否则调用 alloc 方法创建对象
}
复制代码

先来拆解下函数

  • hasCustomAWZ

从 bits 中获取 data,再根据位掩码 RW_HAS_DEFAULT_AWZ 获取是否是默认的 allocWithZone 方法

bool hasCustomAWZ() {
    return ! bits.hasDefaultAWZ();
}
复制代码
bool hasDefaultAWZ() {
    return data()->flags & RW_HAS_DEFAULT_AWZ;
}
复制代码
  • canAllocFast

与hasCustomAWZ的流程差不多,根据位掩码FAST_ALLOC去判断是否支持快速Alloc

bool canAllocFast() {
    assert(!isFuture());
    return bits.canAllocFast();
}
复制代码
bool canAllocFast() {
    return bits & FAST_ALLOC;
}
复制代码
  • initInstanceIsa

简单的初始化工作,代码流程很清晰,不在阐述

inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    assert(!cls->instancesRequireRawIsa());
    assert(hasCxxDtor == cls->hasCxxDtor());

    initIsa(cls, true, hasCxxDtor);
}
复制代码
inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    assert(!isTaggedPointer()); 
    
    if (!nonpointer) {
        isa.cls = cls; // 如果 nonpointer 为 false,则直接把 cls 赋值给 cls
    } else {
        assert(!DisableNonpointerIsa);
        assert(!cls->instancesRequireRawIsa());

        isa_t newisa(0); // 初始化新的 isa

#if SUPPORT_INDEXED_ISA
        assert(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE; // 对 isa 中的一些值进行初始化
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
        isa = newisa; // 将 newisa 赋值给 isa
    }
}
复制代码
  • class_createInstance

如果类不支持 AllocFast,则需要通过 class_createInstance 方法进行对象的创建
alloc出来的对象引用计数是为0

id 
class_createInstance(Class cls, size_t extraBytes)
{
    return _class_createInstanceFromZone(cls, extraBytes, nil);
}
复制代码
static __attribute__((always_inline)) 
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, 
                              bool cxxConstruct = true, 
                              size_t *outAllocatedSize = nil)
{
    if (!cls) return nil;

    assert(cls->isRealized());

    bool hasCxxCtor = cls->hasCxxCtor(); // 获取 cls 及其父类是否有构造函数
    bool hasCxxDtor = cls->hasCxxDtor(); // 获取 cls 及其父类是否有析构函数
    bool fast = cls->canAllocNonpointer(); // 是对 isa 的类型的区分,如果一个类和它父类的实例不能使用 isa_t 类型的 isa 的话,返回值为 false

    size_t size = cls->instanceSize(extraBytes); // 获取需要申请的空间大小
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (!zone  &&  fast) { // 如果 zone 参数为空,且支持 fast,通过 calloc 申请空间并初始化 isa
        obj = (id)calloc(1, size);
        if (!obj) return nil;
        obj->initInstanceIsa(cls, hasCxxDtor);
    } 
    else {
        if (zone) { // 如果 zone 不为空,使用 malloc_zone_calloc 方法申请空间
            obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
        } else { // 否则使用 calloc 方法申请空间
            obj = (id)calloc(1, size);
        }
        if (!obj) return nil;

        // Use raw pointer isa on the assumption that they might be 
        // doing something weird with the zone or RR.
        obj->initIsa(cls); // 初始化 isa
    }

    if (cxxConstruct && hasCxxCtor) {
        obj = _objc_constructOrFree(obj, cls); // 构建对象
    }

    return obj;
}
复制代码

alloc流程图

retainCount

retainCount是获取当前对象的引用计数,源码如下:

- (NSUInteger)retainCount {
    return ((id)self)->rootRetainCount();
}
复制代码
inline uintptr_t 
objc_object::rootRetainCount()
{
    // 如果是 TaggedPointer 类型直接返回
    if (isTaggedPointer()) return (uintptr_t)this;
    // 散列表上锁
    sidetable_lock();
    // 通过 LoadExclusive 方法加载 isa 的值,加锁
    isa_t bits = LoadExclusive(&isa.bits);
    // 解锁
    ClearExclusive(&isa.bits);
    // 如果 isa 不是通过指针方式实现的话
    if (bits.nonpointer) {
        // 获取当前对象的extra_rc,然后加一
        uintptr_t rc = 1 + bits.extra_rc;
        // 如果 isa 散列表中包含有值
        if (bits.has_sidetable_rc) {
            // 则把散列表的值取出并且加上
            rc += sidetable_getExtraRC_nolock();
        }
        // 散列表解锁
        sidetable_unlock();
        // 返回rc
        return rc;
    }
    // 散列表解锁
    sidetable_unlock();
    // 返回散列表中的值
    return sidetable_retainCount();
}
复制代码
  • extra_rc

extra_rc 就是用于保存自动引用计数的标志位,是在 isa_t 结构体中,共有8位。它存储的是对象本身之外的引用计数的数量,所以获取总数的时候要加 1。但 extra_rc 可能不足以存储引用计数,这时候 sidetable 就派上用场了。

  • has_sidetable_rc

has_sidetable_rc 是用于标志是否通过 sidetable 存储引用计数的标志。

  • sidetable_getExtraRC_nolock函数是用于从 sidetable 中获取引用计数信息的方法
size_t 
objc_object::sidetable_getExtraRC_nolock()
{
    assert(isa.nonpointer);
    SideTable& table = SideTables()[this]; // 根据当前的对象找到引用计数存在的 table
    RefcountMap::iterator it = table.refcnts.find(this); // 查找引用计数
    if (it == table.refcnts.end()) return 0; // 如果没找到,返回0
    else return it->second >> SIDE_TABLE_RC_SHIFT; // 如果找到了,通过 SIDE_TABLE_RC_SHIFT 位掩码获取对应的引用计数
}
复制代码
  • sidetable_retainCount
uintptr_t
objc_object::sidetable_retainCount()
{
    // 根据当前的对象找到引用计数存在的 table
    SideTable& table = SideTables()[this];

    size_t refcnt_result = 1; // 设置对象本身的引用计数为1
    
    table.lock();
    RefcountMap::iterator it = table.refcnts.find(this); // 查找引用计数
    if (it != table.refcnts.end()) { // 如果找到了
        // this is valid for SIDE_TABLE_RC_PINNED too
        refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT; // 返回 1 + sidetable 中存储的引用计数
    }
    table.unlock();
    return refcnt_result;
}
复制代码

如果nonpointer 为 true,表示使用的还是老版本中通过指针表示 isa 的方式,这个时候,所有的引用计数全部存在 sidetable 中

retain

retain的主要工作是增加引用计数,源码如下:

- (id)retain {
    return ((id)self)->rootRetain();
}
复制代码
ALWAYS_INLINE id 
objc_object::rootRetain()
{
    return rootRetain(false, false);
}
复制代码
ALWAYS_INLINE id 
objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
    // 判断是否是 TaggedPointer 类型,如果是,则返回本身
    if (isTaggedPointer()) return (id)this;
    // 标记 sideTable 是否处于加锁状态
    bool sideTableLocked = false;
    // 是否需要把引用计数存储在 sideTable 中
    bool transcribeToSideTable = false;

    isa_t oldisa;
    isa_t newisa;

    do {
        transcribeToSideTable = false;
        // 取出isa
        oldisa = LoadExclusive(&isa.bits);
        newisa = oldisa;
        // nonpointer:表示是否对isa开启指针优化 。0代表是纯isa指针,1代表除了地址外,还包含了类的一些信息、对象的引用计数等。
        if (slowpath(!newisa.nonpointer)) { // 如果 isa 是通过指针方式实现的
            ClearExclusive(&isa.bits); // 解锁
            // 如果 tryRetain 为 false 且给 sideTable 加过锁,则为其解锁
            if (!tryRetain && sideTableLocked) sidetable_unlock();
            // 如果 tryRetain 为 ture,则根据调用 sidetable_tryRetain() 结果决定返回 this 或者是 nil。
            if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
            else return sidetable_retain(); // 其余情况直接返回 sidetable_retain 的结果
        }
        if (slowpath(tryRetain && newisa.deallocating)) { // 如果 tryRetain 为 true,且对象正在被销毁
            ClearExclusive(&isa.bits); // 解锁
            // 如果 tryRetain 为 false 且给 sideTable 加过锁,则为其解锁
            if (!tryRetain && sideTableLocked) sidetable_unlock();
            return nil;
        }
        uintptr_t carry;
        newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc++

        if (slowpath(carry)) { // 如果 carry 为 true,要处理引用计数溢出的情况
            // newisa.extra_rc++ overflowed
            if (!handleOverflow) { // 如果 handleOverflow 为 false
                ClearExclusive(&isa.bits); // 解锁
                return rootRetain_overflow(tryRetain); // 跳转执行 rootRetain_overflow
            }
            // Leave half of the retain counts inline and 
            // prepare to copy the other half to the side table.
            // 如果 sidetable_lock 未加锁,则为其加锁
            if (!tryRetain && !sideTableLocked) sidetable_lock();
            sideTableLocked = true;
            transcribeToSideTable = true;
            newisa.extra_rc = RC_HALF; // extra_rc 的值减半
            newisa.has_sidetable_rc = true; // 标记是否在 sidetable 中存有引用计数
        }
    } while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));

    if (slowpath(transcribeToSideTable)) { // 如果需要将部分的引用计数放到 sidetable 中
        // Copy the other half of the retain counts to the side table.
        sidetable_addExtraRC_nolock(RC_HALF);
    }

    if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock(); // 解锁
    return (id)this;
}
复制代码

源码中的注释每一步都写得很清楚了,下面来拆解下函数

  • sidetable_tryRetain
bool
objc_object::sidetable_tryRetain()
{
#if SUPPORT_NONPOINTER_ISA
    assert(!isa.nonpointer);
#endif
    SideTable& table = SideTables()[this];
    
    bool result = true;
    // 通过 this 从 table 中寻找是否有引用计数
    RefcountMap::iterator it = table.refcnts.find(this);
    if (it == table.refcnts.end()) { // 如果查找不到
        table.refcnts[this] = SIDE_TABLE_RC_ONE; // 设置表中的引用计数为 SIDE_TABLE_RC_ONE
    } else if (it->second & SIDE_TABLE_DEALLOCATING) { // 如果对象此时处于 deallocating 状态
        result = false;
    } else if (! (it->second & SIDE_TABLE_RC_PINNED)) { // 如果查找到了,且未溢出,则将引用计数加1。
        it->second += SIDE_TABLE_RC_ONE;
    }
    
    return result;
}
复制代码
  • sidetable_retain
id
objc_object::sidetable_retain()
{
#if SUPPORT_NONPOINTER_ISA
    assert(!isa.nonpointer);
#endif
    SideTable& table = SideTables()[this];
    
    table.lock();
    size_t& refcntStorage = table.refcnts[this];
    if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) { // 如果查找到了,且未溢出,则将引用计数加1。
        refcntStorage += SIDE_TABLE_RC_ONE;
    }
    table.unlock();

    return (id)this;
}
复制代码
  • addc

这个函数的作用就是引用计数加一

static ALWAYS_INLINE uintptr_t 
addc(uintptr_t lhs, uintptr_t rhs, uintptr_t carryin, uintptr_t *carryout)
{
    return __builtin_addcl(lhs, rhs, carryin, carryout);
}
复制代码
  • rootRetain_overflow

当 addc 方法告诉我们上溢出的时候,就调用 rootRetain_overflow 方法,这个方法内部又调用了 rootRetain ,是一个递归调用

NEVER_INLINE id 
objc_object::rootRetain_overflow(bool tryRetain)
{
    return rootRetain(tryRetain, true);
}
复制代码
  • sidetable_addExtraRC_nolock

sidetable_addExtraRC_nolock 函数的功能是把引用计数往散列表里面添加

bool 
objc_object::sidetable_addExtraRC_nolock(size_t delta_rc)
{
    assert(isa.nonpointer);
    SideTable& table = SideTables()[this];

    size_t& refcntStorage = table.refcnts[this];
    size_t oldRefcnt = refcntStorage;
    // isa-side bits should not be set here
    assert((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
    assert((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);

    if (oldRefcnt & SIDE_TABLE_RC_PINNED) return true;
    
    uintptr_t carry;
    size_t newRefcnt = 
        addc(oldRefcnt, delta_rc << SIDE_TABLE_RC_SHIFT, 0, &carry);
    
    if (carry) {
        refcntStorage =
            SIDE_TABLE_RC_PINNED | (oldRefcnt & SIDE_TABLE_FLAG_MASK);
        return true;
    }
    else {
        refcntStorage = newRefcnt;
        return false;
    }
}
复制代码

release

release的作用刚好与retain相反,把对象的引用计数减一,源码如下:

- (oneway void)release {
    ((id)self)->rootRelease();
}
复制代码
ALWAYS_INLINE bool 
objc_object::rootRelease()
{
    return rootRelease(true, false);
}
复制代码
ALWAYS_INLINE bool 
objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
{
    // 判断是否为 TaggedPointer 类型,如果是则返回错误,因为 TaggedPointer 类型没有引用计数
    if (isTaggedPointer()) return false;

    bool sideTableLocked = false;

    isa_t oldisa;
    isa_t newisa;

 retry:
    do {
        oldisa = LoadExclusive(&isa.bits); // 为 isa.bits 加锁
        newisa = oldisa;
        if (slowpath(!newisa.nonpointer)) { // 判断 isa 是否为纯指针实现
            ClearExclusive(&isa.bits); // 解锁isa.bits
            if (sideTableLocked) sidetable_unlock(); // sidetable解锁
            return sidetable_release(performDealloc); // 通过 sidetable_release 为其解锁
        }
        uintptr_t carry;
        newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc--
        // 如果extra_rc==0,extra_rc--会是负数,carry=1
        if (slowpath(carry)) { // 如果发现溢出的情况
            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重新赋值
    newisa = oldisa;
    // 判断是否有引用计数
    if (slowpath(newisa.has_sidetable_rc)) {
        // 有借位保存,rootRelease_underflow重新进入函数
        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;
        }
        // 获取借位引用次数,(获取次数最大RC_HALF)
        // 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.
        // 如果 borrowed > 0 ,即是说散列表中有值
        if (borrowed > 0) {
            // Side table retain count decreased.
            // Try to add them to the inline count.
            // 保存,就是StoreExclusive
            // 如果&isa.bits和oldisa.bits相等,那么就把newisa.bits的值赋给&isa.bits,并且返回true
            newisa.extra_rc = borrowed - 1;  // redo the original decrement too
            // 存储更改后的 isa.bits
            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) {
                // 还是不成功,把次数放回SideTable,重试retry
                // 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)) {
        // 如果对象已经正在释放,报错警告:多次release
        ClearExclusive(&isa.bits);
        if (sideTableLocked) sidetable_unlock();
        return overrelease_error();
        // does not actually return
    }
    newisa.deallocating = true;
    // 保存bits
    if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
    
    if (slowpath(sideTableLocked)) sidetable_unlock();

    __sync_synchronize();
    if (performDealloc) {
        // 调用dealloc
        ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
    }
    return true;
}
复制代码
  • sidetable_release

如果isa为纯指针实现,即isa里面没有引用计数,就直接操作散列表里面的引用计数

uintptr_t
objc_object::sidetable_release(bool performDealloc)
{
#if SUPPORT_NONPOINTER_ISA
    assert(!isa.nonpointer);
#endif
    SideTable& table = SideTables()[this]; // 通过对象获取对应的 table

    bool do_dealloc = false; // 标识是否需要执行 dealloc 方法

    table.lock(); // 加锁
    RefcountMap::iterator it = table.refcnts.find(this); // 通过 this 查找引用计数
    if (it == table.refcnts.end()) { // 未找到
        do_dealloc = true;
        table.refcnts[this] = SIDE_TABLE_DEALLOCATING; // 设为 deallocating 状态
    } else if (it->second < SIDE_TABLE_DEALLOCATING) { // 如果对象处于 deallocating 状态
        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 方法的情况下
        // 执行 dealloc 方法
        ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
    }
    return do_dealloc;
}
复制代码
  • subc

把isa指针里面的引用计数减一

static ALWAYS_INLINE uintptr_t 
subc(uintptr_t lhs, uintptr_t rhs, uintptr_t carryin, uintptr_t *carryout)
{
    return __builtin_subcl(lhs, rhs, carryin, carryout);
}
复制代码
  • rootRelease_underflow

如果有下溢出的情况,就执行rootRelease_underflow,里面的实现就是调用了rootRelease,相当于递归调用

NEVER_INLINE bool 
objc_object::rootRelease_underflow(bool performDealloc)
{
    return rootRelease(performDealloc, true);
}
复制代码
  • sidetable_subExtraRC_nolock

把引用计数里面的值取出来,赋值给isa.extra_rc

size_t 
objc_object::sidetable_subExtraRC_nolock(size_t delta_rc)
{
    assert(isa.nonpointer);
    SideTable& table = SideTables()[this];

    RefcountMap::iterator it = table.refcnts.find(this);
    if (it == table.refcnts.end()  ||  it->second == 0) {
        return 0;
    }
    size_t oldRefcnt = it->second;

    // isa-side bits should not be set here
    assert((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0); // 确保对象不是 deallocating 状态
    assert((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0); // 确保是不是弱饮用

    size_t newRefcnt = oldRefcnt - (delta_rc << SIDE_TABLE_RC_SHIFT); // 散列表的引用计数减去传进来的 delta_rc
    assert(oldRefcnt > newRefcnt);  
    it->second = newRefcnt; // 设置散列表的新引用计数
    return delta_rc;
}
复制代码
  • overrelease_error

如果对象正在析构的时候再次调用release函数就会触发这个方法,

NEVER_INLINE
bool 
objc_object::overrelease_error()
{
    _objc_inform_now_and_on_crash("%s object %p overreleased while already deallocating; break on objc_overrelease_during_dealloc_error to debug", object_getClassName((id)this), this);
    objc_overrelease_during_dealloc_error();
    return false;  // allow rootRelease() to tail-call this
}
复制代码
  • __sync_synchronize

__sync_synchronize 的作用是:
No memory operand will be moved across the operation, either forward or backward. Further, instructions will be issued as necessary to prevent the processor from speculating loads across the operation and from queuing stores after the operation.
简而言之就是,在这个方法调用之后,涉及到内存的运算符都被禁止操作了(因为要进行析构了)。


参考文章

iOS __weak的底层实现
iOS底层原理探索 — weak实现原理
从源码角度看苹果是如何实现 alloc、new、copy 和 mutablecopy 的
从源码角度看苹果是如何实现 retainCount、retain 和 release 的

关注下面的标签,发现更多相似文章
评论