ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

闲聊Objective-C中的weak

2021-05-15 21:02:29  阅读:249  来源: 互联网

标签:函数 对象 weak 地址 引用 Objective 闲聊 指针


前言

在objective-c中,weak几乎无处不在。尤其是定义ivar时,经常要用到这个关键字。用weak修饰的变量,在对象释放之后,对象会自动置为nil。它是怎么做到的呢?在研究它之前,有几个词有必要先了解一下。

关键词

SideTable

SideTable是一个结构体,主要有三个成员。它的作用就是用来管理对象的weak表,引用计数在此先不表。

struct SideTable {
    // 自旋锁
    spinlock_t slock;
    // 引用计数的hash表
    RefcountMap refcnts;
    // 存储对象弱引用指针的hash表
    weak_table_t weak_table;

    ...
}

其中weak表(weak_table)是实现weak功能的核心数据结构。其实也可以理解SideTable是对weak_table的一个封装,方便更好的使用和访问weak表。

weak表

weak表是一个hash表,存储了弱引用对象以及相关的所有弱引用的信息。key为对象的地址,value为指向该对象的weak指针地址数组。

/**
 * The global weak references table. Stores object ids as keys,
 * and weak_entry_t structs as their values.
 */
struct weak_table_t {
    weak_entry_t *weak_entries;
    size_t    num_entries;
    uintptr_t mask;
    uintptr_t max_hash_displacement;
};

抽象一点的话可以像下面这么理解:

f(对象的地址) = 指向该对象的weak指针地址数组

weak_entry_t

weak_entries也是一个hash结构体,它存储的是指向绑定的弱引用对象的所有weak指针的地址。


/**
 * The internal structure stored in the weak references table. 
 * It maintains and stores
 * a hash set of weak references pointing to an object.
 * If out_of_line_ness != REFERRERS_OUT_OF_LINE then the set
 * is instead a small inline array.
 */
#define WEAK_INLINE_COUNT 4

// out_of_line_ness field overlaps with the low two bits of inline_referrers[1].
// inline_referrers[1] is a DisguisedPtr of a pointer-aligned address.
// The low two bits of a pointer-aligned DisguisedPtr will always be 0b00
// (disguised nil or 0x80..00) or 0b11 (any other address).
// Therefore out_of_line_ness == 0b10 is used to mark the out-of-line state.
#define REFERRERS_OUT_OF_LINE 2

struct weak_entry_t {
    DisguisedPtr<objc_object> referent;
    union {
        struct {
            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 {
            // out_of_line_ness field is low bits of inline_referrers[1]
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
        };
    };

    ...
};

weak的实现原理

  • ①初始化时,runtime调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
  • ②objc_initWeak函数会调用storeWeak函数。更新指针指向,创建弱引用表。
  • ③如果weak指针之前指向了一个弱引用对象,则调用weak_unregister_no_lock函数接触该对象和weak指针的绑定。
  • ④如果weak指针需要指向一个新的弱引用对象,则调用weak_register_no_lock函数把weak指针地址添加到以该对象的地址为key的弱引用表里,从而实现绑定。

释放

  • ①对象释放时调用release函数。
  • ②然后调_objc_rootRelease函数。
  • ③接着调rootRelease函数。
  • ④引用计数为0时,执行dealloc函数。
  • ⑤然后调_objc_rootDealloc函数。
  • ⑥接着调rootDealloc函数。
  • ⑦接着调object_dispose函数。
  • ⑧接着调用objc_destructInstance函数。
  • ⑨接着就是一个比较重要的函数clearDeallocating。
  • ⑩接着调用clearDeallocating_slow函数。

clearDeallocating中有两个分支,现实判断对象是否采用了优化isa引用计数,如果没有的话则需要清理对象存储在SideTable中的引用计数数据。如果对象采用了优化isa引用计数,则判断是都有使用SideTable的辅助引用计数(isa.has_sidetable_rc)或者有weak引用(isa.weakly_referenced),符合这两种情况中一种的,调用clearDeallocating_slow方法。

  • ⑪最后调用关键的weak_clear_no_lock函数。从表中通过释放对象的地址拿到存放weak指针地址的弱引用表,然后将所有weak指针地址指向的值赋为nil,并从表中删掉该条记录。

总结

weak功能的实现主要就是通过runtime维护一张hash表,在表里存储对象的地址和指向它的所有weak指针的地址。当存储对象被释放时,通过weak_clear_no_lock函数遍历存储weak指针地址的数组,把weak指针置空,从而避免野指针的问题。(因为对象一般分配在堆上的某块内存,如果在后续的内存分配中,刚好分到了这块地址,程序就会崩溃掉;而基础数据类型一般分配在栈上,栈的内存会由系统自己自动处理,不会造成野指针)

原文地址

标签:函数,对象,weak,地址,引用,Objective,闲聊,指针
来源: https://blog.51cto.com/u_15010671/2778152

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有