美文网首页
OC内存管理之引用计数

OC内存管理之引用计数

作者: 苏沫离 | 来源:发表于2020-05-08 20:43 被阅读0次

Objective-C 的内存管理是通过引用计数实现的!若引用计数为正,则对象存活;若引用计数降为 0,运行时系统会释放它的内存!

objc 源码中使用散列表 SideTable 辅助管理对象的强引用计数和弱引用依赖:

//散列表 SideTable :主要用于辅助管理对象的 强引用计数 和 弱引用依赖
struct SideTable {
    spinlock_t slock;// 保证操作线程安全的自旋锁;
    RefcountMap refcnts;//引用计数的Map表 key-value:当isa中extra_rc不足以保存时,使用散列表保存refcnts.find(obj)
    weak_table_t weak_table; //弱引用表
 }

自旋锁spinlock_t:如果把所有的类都存储在同一个表中,改动任何一个类都需要对整个表上锁,这导致操作效率和查询效率都很低。因此使用 Hash 数组 SideTables 管理多个SideTable,每个SideTable存储一部分对象,操作单个SideTable并不会影响到其它的表!

1、弱引用表 weak_table_t

在ARC模式中使用关键字weak来处理对象之间的互相强引用导致的内存泄漏问题,主要是因为使用weak修饰变量不会导致引用计数的增加、不会影响对象的生命周期、而且在对象释放之后会将自动指针置空避免也指针访问问题!那么系统是如何管理weak变量呢?

散列表SideTable中的 weak_table_t被设计用来管理指向对象的弱引用指针:

/** 全局的弱引用表是一个由自旋锁控制的哈希表 : 程序中所有的弱引用都在该表中进行存储;
 *  该表以键值对的形式存储,对象作为 key,value 是结构 weak_entry_t
 */
 struct weak_table_t {
     weak_entry_t *weak_entries;//存储所有指向某个对象的weak指针
     size_t    num_entries;//哈希表的容量大小
     uintptr_t mask;//参与判断引用计数辅助量
     uintptr_t max_hash_displacement;//hash key 最大偏移值 : hash冲撞时最大尝试次数,用于优化搜索算法
 };

哈希表weak_table_t并不直接管理弱引用指针,而是通过 weak_entry_t 来管理!在weak_table_t表中,每个对象对应一个 weak_entry_t,而每个 weak_entry_t 中存储着指向该对象的所有弱引用指针。

/** 用来存储某一对象的所有弱引用指针
 * @param referent 被弱引用指针指向的对象,
 * @note 该结构使用两种存储方案存储弱引用指针:
 *       case_1:弱引用指针数量 < 4 : 使用静态数组 inline_referrers 进行保存,同时 out_of_line_ness = 0;
 *       case_2:弱引用指针数量 > 4 : 使用二维数组 referrers 保存, out_of_line_ness = 2;
 **/
struct weak_entry_t {
    DisguisedPtr<objc_object> referent;
    union {
        struct {//当弱引用指针个数大于 WEAK_INLINE_COUNT 时,使用二维指针数组进行存储
            weak_referrer_t *referrers;
            uintptr_t        out_of_line_ness : 2;
            uintptr_t        num_refs : PTR_MINUS_2;
            uintptr_t        mask;
            uintptr_t        max_hash_displacement;
        };
        struct {//当弱引用指针个数小于 WEAK_INLINE_COUNT 时,使用一维数组进行存储
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
        };
    };
    bool out_of_line() {//判断当前是否是离线存储
        return (out_of_line_ness == REFERRERS_OUT_OF_LINE);
    }
    weak_entry_t& operator=(const weak_entry_t& other) {//重载运算符=
        memcpy(this, &other, sizeof(other));
        return *this;
    }
    //第一个弱引用指针使用该方法存储
    weak_entry_t(objc_object *newReferent, objc_object **newReferrer) : referent(newReferent){
        inline_referrers[0] = newReferrer;
        for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
            inline_referrers[i] = nil;
        }
    }
};
1.1、弱引用表weak_table_t的管理
1.1.1、弱引用表的插入、删除、查询

通过插入、删除、查询操作,可以管理弱引用表weak_table_t的某个对象 weak_entry_t

/** 将 new_entry 添加到弱引用表中
 * @note 不会检查引用是否已经在表中
 */
static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry);

/** 从弱引用表移除弱引用 weak_entry_t
 */
static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry);

/** 在弱引用表查询指定对象的 weak_entry_t
 * @param referent 指定对象,不能为 nil
 * @return 如果弱引用表没有该对象的 weak_entry_t ,则返回 NULL
 */
static weak_entry_t *weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent);
1.1.2、弱引用表容量的扩增与缩减

weak_table_t 容量过满或者过空时,需要及时调整其大小,以优化内存的使用率,提高运行效率!

/* 调整弱引用表中的大小
 * @param new_size 新的大小
 */
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_table->weak_entries = new_entries;
    
    /* 哈希表可能会有 hash 碰撞,而 weak_table_t 使用了开放寻址法来处理碰撞。
     * 如果发生碰撞的话,将寻找相邻(如果已经到最尾端的话,则从头开始)的下一个空位。
     * max_hash_displacement 记录当前 weak_table 最大的偏移值,即hash函数计算的位置和实际存储位置的最大偏差。
     */
    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_t 的空间,扩充条件是表的 3/4及以上的空间已经被使用。
 * @note 该表初始化大小是 64 个 weak_entry_t 的空间,每次扩充后的空间都是当前空间的两倍,即2的N次方(N>=6)
 */
static void weak_grow_maybe(weak_table_t *weak_table){
    size_t old_size = TABLE_SIZE(weak_table);
    if (weak_table->num_entries >= old_size * 3 / 4) {
        weak_resize(weak_table, old_size ? old_size*2 : 64);
    }
}

/** 缩小 weak_table_t 的空间,缩小的条件是表的容量不小于1024个weak_entry_t的空间,并且低于1/16的空间被占用
 * 缩小后的空间是当前空间的1/8。
 */
static void weak_compact_maybe(weak_table_t *weak_table){
    size_t old_size = TABLE_SIZE(weak_table);
    if (old_size >= 1024  && old_size / 16 >= weak_table->num_entries) {
        weak_resize(weak_table, old_size / 8);
    }
}
1.2、weak_entry_t的插入与删除

weak_entry_t使用数组存储指向某个对象的所有弱引用指针!Runtime 提供了一些函数可以将弱引用指针插入这个数组、从数组移除指定的弱引用指针、扩充数组容量等操作

/** 将弱引用指针存储至weak_entry_t的数组中
 *  该函数主要做了以下几件事:
 *    1、当前没有使用离线存储,将 new_referrer 保存至数组 inline_referrers的空余位置;
 *       如果没有空余位置,将数组 new_referrer 的元素拷至数组 referrers 并设置为离线存储;
 *    2、如果数组 new_referrer 当前已经使用了总量的3/4 ,则需要扩充容量,并将指针 new_referrer 存储至 weak_entry_t
 *    3、如果数组 new_referrer 当前还未超出总量的3/4 ,则直接将指针 new_referrer 存储至 weak_entry_t
 */
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)

/** 从指定 weak_entry_t 实例中的数组移出某个弱引用指针
 * 该函数主要做了以下几件事:
 *  1、如果使用非离线存储,则遍历静态数组 inline_referrers,找到 old_referrer 则移出数组,否则继续执行;
 *  2、如果使用离线存储:使用 w_hash_pointer(old_referrer) & (entry->mask)获取起始索引,
 *                  遍历entry->referrers,找到之后置空,并entry->num_refs自减.
 */
static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)

/** 扩容并增加新的弱引用指针,最终的存储还是依靠append_referrer() 函数完成
 */
static void grow_refs_and_insert(weak_entry_t *entry,objc_object **new_referrer)

1.3、weak变量的管理机制

疑问:弱引用指针是如何一步步的存储到weak_entry_t的数组中,被weak_table_t管理的呢?

弱引用指针的使用有多种情况:

  • 1、初始化一个弱引用指针并赋值: __weak MyModel *weakModel = model;
  • 2、将一个已声明的弱引用指针重新赋值:*weakModel = model;
  • 3、被弱引用的对象释放 dealloc

我们分别看下它们的底层调用有何不同!

1.3.1、注册一个有效的弱引用指针

注册一个有效的弱引用指针,底层函数调用流程:

- __weak MyModel *weakModel = model;
└── id objc_initWeak(id *location, id newObj)
    └─ id storeWeak(id *location, objc_object *newObj)
       └─ id  weak_register_no_lock()
          └─ void append_referrer()
             ├─ void weak_grow_maybe()
             ├─ void weak_entry_insert()
I、 初始化一个新的弱引用指针
/** 初始化一个新的弱引用指针指向对象的地址
 * @param location 弱引用指针的内存地址
 * @param newObj 弱指针指向的新对象
 * @note 该函数不是线程安全的
 * @note 函数有一个前提条件:就是object必须是一个没有被注册为__weak对象的有效指针
 */
id objc_initWeak(id *location, id newObj){
    if (!newObj) {//判断原始引用对象是否为空
        *location = nil;
        return nil;
    }
    //调用 objc_storeWeak() 函数,更新指针指向,创建对应的弱引用表
    return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>(location, (objc_object*)newObj);
}
II、新指针指向,创建对应的弱引用表
/** 更新指针指向,创建对应的弱引用表
 * 该函数主要做了以下几件事:
 *   1、分别获取新、旧值相关联的 SideTable
 *   2、如果有旧值就调用 weak_unregister_no_lock() 函数,从旧值的 weak_entry_t 数组中移出旧指针
 *   3、如果有新值就调用 weak_register_no_lock() 函数分配新值,并标记新值存在弱引用
*/
template <HaveOld haveOld, HaveNew haveNew,CrashIfDeallocating crashIfDeallocating>
static id storeWeak(id *location, objc_object *newObj){
    assert(haveOld  ||  haveNew);//断言新值和旧值至少有一个是存在
    if (!haveNew) assert(newObj == nil);
    
    //在类没有完成 +initialized 方法之前调用 weakStore 时,作为初始化的标识
    Class previouslyInitializedClass = nil;
    id oldObj;
    SideTable *oldTable;
    SideTable *newTable;
        
retry:
    /******************* 分别获取新旧值相关联的引用表 *****************/
    if (haveOld) {
        oldObj = *location;//获取弱引用指针的旧指向
        oldTable = &SideTables()[oldObj];//获取oldObj对应的弱引用表
    } else {
        oldTable = nil;
    }
    if (haveNew) {
        newTable = &SideTables()[newObj];//获取newObj对应的弱引用表
    } else {
        newTable = nil;
    }
    SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);//上锁
    if (haveOld && *location != oldObj) {//如果旧值改变就重新获取旧值相关联的表
        SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
        goto retry;
    }
    // 如果有新值,判断新值所属的类是否已经初始化,如果没有初始化,则先执行初始化,防止+initialize内部调用 storeWeak() 产生死锁
    if (haveNew  &&  newObj) {
        Class cls = newObj->getIsa();
        if (cls != previouslyInitializedClass  && !((objc_class *)cls)->isInitialized()){
            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
            _class_initialize(_class_getNonMetaClass(cls, (id)newObj));
            // 如果这个类在它自身的一个实例上调用storeWeak()进行 +initialize,那么我们可以继续,但是它将显示为正在初始化,并且还没有初始化到上面的检查中。
            previouslyInitializedClass = cls;
            goto retry;
        }
    }
    /*********** 如果旧值存在,则从旧值的 weak_entry_t 数组中移出旧指针 **********/
    if (haveOld) {
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    }
    /*********** 如果有新值,绑定新值与弱引用指针 **********/
    if (haveNew) {
        newObj = (objc_object *)weak_register_no_lock(&newTable->weak_table, (id)newObj, location,crashIfDeallocating);
        if (newObj  &&  !newObj->isTaggedPointer()) {
            newObj->setWeaklyReferenced_nolock();//标记新值存在弱引用
        }
        *location = (id)newObj;//不要在其他地方设置 *location。那会引起数据竞争。
    }else {
        // 没有新值,存储没有更改。
    }
    SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
    return (id)newObj;
}

注意: 使用模板参数进行常量参数传递是为了优化性能,预判了大概率会发生的事情优先处理;

  • param HaveOld 是否有旧的引用,如果有旧的引用则需要释放;
  • param HaveNew 是否有新的引用,如果有则需要存储新的引用;
  • param crashifdeallocatetrue,则当newObj正在释放或newObj的类不支持弱引用时,进程将停止;为false则存储nil
  • case1 初始化一个弱引用指针并赋值,该指针没有旧值:HaveOld=false,haveNew=true
  • case2 将弱引用指针指向nil,该指针没有新值:HaveOld=true,haveNew=false
  • case3 将一个指向其它对象的弱引用指针重新赋值:HaveOld=true,haveNew=true
III、向弱引用表里添加新的弱应用指针
/** 向弱引用表里添加新的弱应用指针
 * 该函数主要做了以下几个事情:
 *  1、如果被引用对象正在释放,则不能再添加弱应用指针
 *  2、在弱引用表查询指定对象的 weak_entry_t
 *    2.1、如果查到 weak_entry_t ,则将弱引用指针存储至 weak_entry_t
 *    2.2、如果没有查到 weak_entry_t,新建一个 weak_entry_t 并存储弱指针,将 weak_entry_t 存储在弱引用表
 * @param referent 被引用对象,不能为空、不能是TaggedPointer
 * @note 在 -dealloc 方法中,不能增加新的弱引用指针,否则会报错.
 */
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;//强转referrer_id为二级指针
    if (!referent||referent->isTaggedPointer()) return referent_id;
    
    /*********************** 如果被引用对象正在释放,则不能再添加弱应用指针 ***********************/
    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;
        }
    }
    
    /*********************** 在弱引用表查询指定对象的 weak_entry_t ***********************/
    weak_entry_t *entry;
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
         /********* 将弱引用指针referrer存储到对象referent关联的weak_entry_t中 *************/
        append_referrer(entry, referrer);
    } else {//在weak_table中未找到referent对应的weak_entry_t
        /********* 新建一个 weak_entry_t 并存储弱指针,将 weak_entry_t 存储在弱引用表 *************/
        weak_entry_t new_entry(referent, referrer);//给对象referent创建一个新的引用列表
        weak_grow_maybe(weak_table);// weak_table 增加内存
        weak_entry_insert(weak_table, &new_entry);//把referent的引用列表加入到weak_table中
    }
    // Do not set *referrer.
    // objc_storeWeak() requires that the value not change.
    return referent_id;
}
IV、将弱引用指针存储至weak_entry_t的数组中
/** 将弱引用指针存储至weak_entry_t的数组中
 *  该函数主要做了以下几件事:
 *    1、当前没有使用离线存储,将 new_referrer 保存至数组 inline_referrers的空余位置;
 *       如果没有空余位置,将数组 new_referrer 的元素拷至数组 referrers 并设置为离线存储;
 *    2、如果数组 new_referrer 当前已经使用了总量的3/4 ,则需要扩充容量,并将指针 new_referrer 存储至 weak_entry_t
 *    3、如果数组 new_referrer 当前还未超出总量的3/4 ,则直接将指针 new_referrer 存储至 weak_entry_t
 */
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer){
    //在增加新的弱引用指针之前使用非离线存储弱引用指针:使用静态数组inline_referrers来进行存储
    if (!entry->out_of_line()) {
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {//遍历数组inline_referrers,是否存在空位置
            if (entry->inline_referrers[i] == nil) {//将新的弱引用指针存储在该数组空位置上,并返回
                entry->inline_referrers[i] = new_referrer;
                return;
            }
        }
        // 在静态数组中没有可用的存储位置,需要开辟离线空间
        weak_referrer_t *new_referrers = (weak_referrer_t *)calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
        
        //将之前inline_referrers数组的元素复制到数组 new_referrers 中
        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());//断言:代码执行到这个位置时 entry 应该是离线存储
    
    if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {//如果当前已经使用了总量的3/4,则扩容并添加新的引用,并返回
        return grow_refs_and_insert(entry, new_referrer);
    }
    
    //如果当前已经使用量小于总量的3/4,则直接添加
    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碰撞
        hash_displacement++;
        index = (index+1) & entry->mask;
        if (index == begin) bad_weak_table(entry);
    }
    //更新存储时最大的hash碰撞次数,由于优化取值时算法减少搜索次数
    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++;
}

至此,Runtime 已经初始化一个新的弱引用指针,并将它存储在散列表 SideTable!

1.3.2、将被注册的弱引用指针重新赋值
-  weakModel = model;
└── id objc_storeWeak(id *location, id newObj)
    └─ id storeWeak(id *location, objc_object *newObj)
       └─ void  weak_unregister_no_lock()
          └─ void remove_referrer()
             └─void void weak_entry_remove()
       └─ id  weak_register_no_lock()
          └─ void append_referrer()
             ├─ void weak_grow_maybe()
             ├─ void weak_entry_insert()

将已经被注册的弱引用指针再次指向别的变量,会通过objc_storeWeak()函数调用storeWeak()函数!在storeWeak()函数中,首先需要将被注册的弱引用指针从被引用变量weak_entry_t的数组中移除;然后才可以将该弱引用指针与新变量重新关联!

/** 移除指定的弱引用指针:
 * 该函数主要做了以下几件事:
 *    1、在 weak_table 中查找对应的 weak_entry_t;
 *    2、从 weak_entry_t 的数组中移除referrer指针;
 *    3、判断 weak_entry_t 的数组是否为空,若不再存储弱引用指针,则从弱引用表中移除 weak_entry_t;
 */
void weak_unregister_no_lock(weak_table_t *weak_table, id referent_id,id *referrer_id){
    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;
    weak_entry_t *entry;
    if (!referent) return;
    /**************** 在弱引用表查询指定对象的 weak_entry_t *************/
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        /********** 从指定 weak_entry_t 实例中的数组移出某个弱引用指针 **********/
        remove_referrer(entry, referrer);
        /********** 若 weak_entry_t 的数组为空,则从弱引用表中移除 weak_entry_t **********/
        bool empty = true;//判断移除指针之后entry数组是否为空
        if (entry->out_of_line()  &&  entry->num_refs != 0) {
            empty = false;
        }else {
            for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
                if (entry->inline_referrers[i]) {
                    empty = false; 
                    break;
                }
            }
        }
        if (empty) {//如果移出指针之后entry数组为空,则从弱引用表中移除entry
            weak_entry_remove(weak_table, entry);
        }
    }
}
1.3.3、弱引用变量 dealloc

当对象调用 -dealloc 方法时,Runtime 会从弱引用表清空指向该对象的所有弱引用指针:

-  [weakModel dealloc];
  └── [NSObject dealloc];
    └─ void _objc_rootDealloc(id obj)
       └─ void objc_object::rootDealloc()
          └─ id object_dispose(id obj)
             └─ void *objc_destructInstance(id obj)
                └─ void objc_object::clearDeallocating()
                     └─ void objc_object::clearDeallocating_slow()
                         └─ void  weak_clear_no_lock()

关键的函数是 weak_clear_no_lock()

/** 当对象调用 -dealloc 方法时,从弱引用表清空某个对象的所有弱引用指针
 */
void  weak_clear_no_lock(weak_table_t *weak_table, id referent_id){
    objc_object *referent = (objc_object *)referent_id;
    weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
    if (entry == nil) {//若entry为空则证明当前对象不存在弱引用指针.
        return;
    }
    weak_referrer_t *referrers;//声明一个数组用来存储所有指向该对象的弱引用指针
    size_t count;//数组容量
    if (entry->out_of_line()) {//使用离线存储弱引用指针
        referrers = entry->referrers;
        count = TABLE_SIZE(entry);
    } else { //使用内部静态数组存储弱引用指针
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }
    //遍历数组,将所有指向该对象的弱引用指针全部指向 nil
    for (size_t i = 0; i < count; ++i) {
        objc_object **referrer = referrers[i];
        if (referrer) {
            if (*referrer == referent) {//当前弱指针指向对象referent
                *referrer = nil;//置空弱引用指针
            }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();
            }
        }
    }
    weak_entry_remove(weak_table, entry); //从weak_table中移除entry
}

2、强引用表 RefcountMap

2.1、+alloc 方法
-  [MyModel alloc];
  └── id objc_alloc(Class cls)
    └─ id callAlloc()
       └─ [NSObject alloc];
          └─ id _objc_rootAlloc(Class cls)
             └─ id callAlloc()
                └─ id class_createInstance()
                     └─ id _class_createInstanceFromZone()
2.2、-retain 方法
/** 对象的引用计数 +1
 * 该函数主要做了以下几件事:
 *  1、通过对象内存地址,在SideTables找到对应的SideTable
 *  2、通过对象内存地址,在refcnts中取出引用计数
 *  3、判断引用计数是否增加到最大值,如果没有,则 +4
 */
id objc_object::sidetable_retain(){
#if SUPPORT_NONPOINTER_ISA
    assert(!isa.nonpointer);
#endif
    /****************** 在SideTables找到对应的SideTable ******************/
    SideTable& table = SideTables()[this];
    table.lock();
    /****************** 在 RefcountMap 中取出引用计数 ******************/
    size_t& refcntStorage = table.refcnts[this];
    if (!(refcntStorage & SIDE_TABLE_RC_PINNED)) {
        /****************** 没有到最大值,1 则+4 ******************/
        refcntStorage += SIDE_TABLE_RC_ONE;
    }
    table.unlock();
    return (id)this;
}
2.3、-release 方法
/** 对象的引用计数 -1
 * 该函数主要做了以下几件事:
 *   1、通过对象内存地址,在 SideTables 找到对应的SideTable
 *   2、通过对象内存地址,在refcnts中取出引用计数
 *   3、根据当前引用计数分别做出对应处理:
 *      3.1、没有引用计数,标记为正在释放状态
 *      3.2、强引用计数为 0 ,不做操作
 *      3.3、没有达到最高位,则引用计数 -1
 *   4、如果需要释放,则调用 -dealloc 方法
 */
uintptr_t objc_object::sidetable_release(bool performDealloc){
#if SUPPORT_NONPOINTER_ISA
    assert(!isa.nonpointer);
#endif
    /****************** 在 SideTables 找到对应的SideTable ******************/
    SideTable& table = SideTables()[this];
    bool do_dealloc = false;
    table.lock();
    /****************** 在 RefcountMap 中取出引用计数 ******************/
    RefcountMap::iterator it = table.refcnts.find(this);
    if (it == table.refcnts.end()) {
        /* table.refcnts.end()表示使用一个iterator迭代器到达了end()状态
         * end() 状态表示从头开始查找,一直找到最后都没有找到
         * 该条 if 语句表示查找到最后都没找到引用计数表 RefcountMap
         */
        do_dealloc = true;
        table.refcnts[this] = SIDE_TABLE_DEALLOCATING;//标记对象为正在释放
    } else if (it->second < SIDE_TABLE_DEALLOCATING) {
        //高位的引用计数位都是0,低位的弱引用标记位可能有弱引用为 1、也可能没有弱引用为 0
        do_dealloc = true;
        it->second |= SIDE_TABLE_DEALLOCATING; //不会影响 弱引用标记位
    } else if (!(it->second & SIDE_TABLE_RC_PINNED)) {
        it->second -= SIDE_TABLE_RC_ONE; //引用计数 -1
    }
    table.unlock();
    if (do_dealloc  &&  performDealloc) {//如果需要释放对象,则调用dealloc
        ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
    }
    return do_dealloc;
}

相关文章

  • 内存管理

    OC对象的内存管理 在iOS中,使用引用计数来管理OC对象的内存 一个新创建的OC对象引用计数默认是1,当引用计数...

  • Effective Objective-C 2.0笔记(四)

    第五章 内存管理 第29条:理解引用计数 OC使用引用计数管理内存,引用计数机制通过递增递减的计数器来管理内存。对...

  • OC类对象的内存管理、AutoreleasePoolPage

    内存管理: 在iOS中,使用引用计数来管理OC对象的内存 一个新创建的OC对象引用计数默认是1,当引用计数减为0,...

  • 内存管理

    OC对象的内存管理 1、在iOS中,使用引用计数来管理OC对象的内存,一个新创建的OC对象引用计数默认是1,当引用...

  • 读《Objective-C高级编程..》笔记

    第一章、自动引用计数 1、内存管理/引用计数 OC中通过引用计数来进行内存管理(其他的内存管理方式有垃圾回收等) ...

  • iOS的内存管理

    iOS OC对象的内存管理 在iOS中,使用引用计数来管理OC对象内存 一个新创建的OC对象引用计数默认是1,当引...

  • OC的内存管理

    1、OC的内存管理 OC是通过引用计数进行内存管理的,其核心思想遵循“谁创建谁释放;谁引用谁管理”。 OC的内存管...

  • 内存管理

    引用计数 OC是通过控制对象的引用计数来管理内存的。 内存管理原则:谁申请,谁释放 alloc / new / ...

  • iOS 开发 基础理论知识一(面试可用)

    1. 内存管理 在iOS 开发中, 使用引用计数来管理OC对象的内存,新创建的OC对象的引用计数是1. 调...

  • 浅谈OC内存管理

    说到OC的内存管理, 就不得不说一说OC的内存管理机制, OC是通过引用计数, 来决定对象是否释放, 引用计数为0...

网友评论

      本文标题:OC内存管理之引用计数

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