当前位置: 首页 > news >正文

weak的实现原理

其实好多技术我们用的都很多,但是如果展开其中的细节原理,不一定能说的清楚。今天就说一下我们常用的属性修饰词weak

例子

@interface Person : NSObject
@property (nonatomic, strong) Person *friend;
@end

@implementation Person
@end

int main() {
    Person *person1 = [[Person alloc] init];
    Person *person2 = [[Person alloc] init];
    
    person1.friend = person2;
    person2.friend = person1;
    
    // 使用 weak 防止循环引用
    __weak Person *weakPerson = person1;
    person1 = nil;  // 这里 person1 被释放,weakPerson 自动变为 nil
    
    NSLog(@"%@", weakPerson); // 输出:null
    
    return 0;
}

在这个例子中,person1person2 之间互相引用。如果我们使用 strong 修饰符,它们会互相持有对方的强引用,从而形成循环引用,导致它们无法释放。但是,当我们将 person1 赋值给 weakPerson 后,weakPerson 不增加 person1 的引用计数,因此当 person1 被置为 nil 时,weakPerson 也会自动被置为 nil

这背后的机制涉及 Side Table 结构和 objc_storeWeakobjc_loadWeak 这两个函数的作用。

核心数据结构

weak 机制主要依赖于两个核心的数据结构:SideTableweak_table_t

1. SideTable

在 Objective - C 运行时中,存在多个 SideTable 实例,这些实例被组织成一个全局的哈希表。每个 SideTable 结构体包含三个主要成员:

struct SideTable {
    spinlock_t slock;  // 自旋锁,用于保证线程安全
    RefcountMap refcnts;  // 引用计数表,记录对象的引用计数
    weak_table_t weak_table;  // 弱引用表,记录对象的弱引用信息
};
2. weak_table_t

weak_table_tSideTable 中的弱引用表,其结构如下:

struct weak_table_t {
    weak_entry_t *weak_entries;  // 弱引用条目数组
    size_t    num_entries;  // 弱引用条目数量
    uintptr_t mask;  // 哈希表的掩码,用于计算哈希索引
    uintptr_t max_hash_displacement;  // 最大哈希冲突位移
};
  • weak_entries:是一个指向 weak_entry_t 数组的指针,每个 weak_entry_t 记录了一个对象的所有弱引用信息。
  • num_entries:表示当前弱引用条目的数量。
  • mask:用于计算对象在 weak_entries 数组中的哈希索引。
  • max_hash_displacement:记录了在处理哈希冲突时的最大位移量。
3. weak_entry_t

weak_entry_t 结构体用于存储一个对象的所有弱引用信息,其结构如下:

typedef 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 {
            // 内联存储,用于存储少量弱引用
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
        };
    };
} weak_entry_t;
  • referent:是一个伪装指针,指向被引用的对象。
  • referrers:是一个指向 weak_referrer_t 数组的指针,weak_referrer_t 实际上就是 objc_object ** 类型,用于存储所有指向该对象的弱引用指针。
  • num_refs:记录了当前对象的弱引用指针数量。

实现流程

1. 弱引用的创建

当使用 __weak 修饰一个变量并指向一个对象时,会调用 objc_initWeak 函数,该函数的主要流程如下:

  • 检查对象是否为 nil,如果为 nil,则直接将弱引用指针置为 nil
  • 调用 storeWeak 函数,在 SideTable 中查找对应的 weak_table_t,并在其中为对象创建一个 weak_entry_t 条目,将弱引用指针添加到该条目的 referrers 数组中。
2. 弱引用的销毁

当被引用的对象即将被释放时,会调用 objc_destroyWeak 函数,该函数的主要流程如下:

  • 调用 storeWeak 函数,在 SideTable 中查找对应的 weak_table_tweak_entry_t 条目。
  • weak_entry_treferrers 数组中移除该弱引用指针。
  • 如果 weak_entry_t 中的弱引用指针数量变为 0,则从 weak_table_t 中移除该条目。
3. 对象释放时弱引用的置为 nil

当对象的引用计数变为 0 并即将被释放时,会调用 objc_dealloc 函数,该函数会进一步调用 weak_clear_no_lock 函数,该函数的主要流程如下:

  • SideTable 中查找对应的 weak_table_tweak_entry_t 条目。
  • 遍历 weak_entry_treferrers 数组,将所有弱引用指针置为 nil
  • weak_table_t 中移除该 weak_entry_t 条目。

相关文章:

  • 3.3.4 VO-O语法- 算子分类(一)
  • Netty的基本架构详解
  • Next.js 15【实用教程】2025最新版
  • BGP配置华为——路由汇总
  • 计算机网络原理习题一
  • 《网络编程卷2:进程间通信》第七章:同步机制深度解析与多场景实践
  • #渗透测试#批量漏洞挖掘#AJ-Report开源数据大屏存在远程命令执行漏洞
  • 数据结构中的邻接矩阵
  • Wireshark TS | 再谈虚假的 TCP Spurious Retransmission
  • QT笔记——QRadioButton
  • VRPTW 问题与新兴技术结合的创新方向及具体案例
  • 借3D视觉定位东风,汽车零部件生产线实现无人化的精准飞跃
  • Ubuntu设置docker代理报网络错误
  • 【Python】条件循环
  • tcp/ip网络模型
  • C# Dictionary的实现原理
  • 傅里叶变换推导
  • 清理docker/podman的存储空间
  • Effective Objective-C 2.0 读书笔记——内存管理(上)
  • 在vuejs项目中使用momentjs获取今日、昨日、本周、下周、本月、上月、本季度、上季度、本年、去年等日期
  • “GoFun出行”订单时隔7年扣费后续:平台将退费,双方已和解
  • 又一例!易方达基金张坤卸任副总职务,将专注于投资管理工作
  • 征稿启事|澎湃·镜相第三届非虚构写作大赛暨2026第六届七猫现实题材征文大赛
  • 百色一女子称家委会强制排班被迫抱婴儿校门口站岗?区教育局:自愿参与
  • 财政部党组召开2025年巡视工作会议暨第一轮巡视动员部署会
  • 来沪一个月几乎未花住宿钱,女子虚构卫生问题屡薅酒店羊毛被刑拘