iOS 底层解析weak的实现原理

2023-11-01

参考地址---------:http://www.cocoachina.com/ios/20170328/18962.html

weak 实现原理的概括

Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址数组,为什么是指针的地址而不是直接是指针的数组?我认为只得到指针没法做后续更改指针的指向,比如置nil,或是内部算法,整改指向都受限。

weak 的实现原理可以概括一下三步:

1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。

2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。

3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

下面将开始详细介绍每一步:

1、初始化时:runtime会调用objc_initWeak函数,objc_initWeak函数会初始化一个新的weak指针指向对象的地址。

示例代码:

1

2

3

4

{

    NSObject *obj = [[NSObject alloc] init];

    id __weak obj1 = obj;

}

当我们初始化一个weak变量时,runtime会调用 NSObject.mm 中的objc_initWeak函数。这个函数在Clang中的声明如下:

1

id objc_initWeak(id *object, id value);

而对于 objc_initWeak() 方法的实现

1

2

3

4

5

6

7

8

9

10

11

12

id objc_initWeak(id *location, id newObj) {

// 查看对象实例是否有效

// 无效对象直接导致指针释放

    if (!newObj) {

        *location = nil;

        return nil;

    }

    // 这里传递了三个 bool 数值

    // 使用 template 进行常量参数传递是为了优化性能

    return storeWeakfalse/*old*/true/*new*/true/*crash*/>

    (location, (objc_object*)newObj);

}

可以看出,这个函数仅仅是一个深层函数的调用入口,而一般的入口函数中,都会做一些简单的判断(例如 objc_msgSend 中的缓存判断),这里判断了其指针指向的类对象是否有效,无效直接释放,不再往深层调用函数。否则,object将被注册为一个指向value的__weak对象。而这事应该是objc_storeWeak函数干的。

objc_initWeak、objc_storeWeak  大致做了如下图相关操作,更新维护两张表,还需要加锁保证多线程一致性,更新新旧哈希表。

QQ截图20170327155908.png

 

 

2、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

当weak引用指向的对象被释放时,又是如何去处理weak指针的呢?当释放对象时,其基本流程如下:

1、调用objc_release

2、因为对象的引用计数为0,所以执行dealloc

3、在dealloc中,调用了_objc_rootDealloc函数

4、在_objc_rootDealloc中,调用了object_dispose函数

5、调用objc_destructInstance

6、最后调用objc_clear_deallocating

重点看对象被释放时调用的objc_clear_deallocating函数。该函数实现如下:

1

2

3

4

5

6

7

void  objc_clear_deallocating(id obj) 

{

    assert(obj);

    assert(!UseGC);

    if (obj->isTaggedPointer()) return;

   obj->clearDeallocating();

}

也就是调用了clearDeallocating,继续追踪可以发现,它最终是使用了迭代器来取weak表的value,然后调用weak_clear_no_lock,然后查找对应的value,将该weak指针置空,weak_clear_no_lock函数的实现如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

/**

 * Called by dealloc; nils out all weak pointers that point to the

 * provided object so that they can no longer be used.

 *

 * @param weak_table

 * @param referent The object being deallocated.

 */

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) {

        /// XXX shouldn't happen, but does with mismatched CF/objc

        //printf("XXX no entry for clear deallocating %p\n", referent);

        return;

    }

    // zero out references

    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;

    }

 

    for (size_t i = 0; i < count; ++i) {

        objc_object **referrer = referrers[i];

        if (referrer) {

            if (*referrer == 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);

}

objc_clear_deallocating该函数的动作如下:

1、从weak表中获取废弃对象的地址为键值的记录

2、将包含在记录中的所有附有 weak修饰符变量的地址,赋值为nil

3、将weak表中该记录删除

4、从引用计数表中删除废弃对象的地址为键值的记录

看了objc-weak.mm的源码就明白了:其实Weak表是一个hash(哈希)表,然后里面的key是指向对象的地址,Value是Weak指针的地址的数组。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

iOS 底层解析weak的实现原理 的相关文章

  • Linux:入门学习知识及常见指令

    文章目录 入门介绍 操作系统的概念 Linux机器的使用 Linux上的指令 对文件知识的补充 文件的定义和一些含义 文件和目录的存储 绝对路径和相对路径 ls指令 pwd指令 cd指令 touch指令 mkdir指令 rmdir指令 rm
  • 嵌套循环以及break和continue

    嵌套循环试验 打印九九表 row 1 line 1 while row lt 9 line 1 while line lt row print f line row row line end line 1 print row 1 break
  • USB2.0协议分析

    前言 为了方便查看博客 特意申请了一个公众号 附上二维码 有兴趣的朋友可以关注 和我一起讨论学习 一起享受技术 一起成长 一 USB硬件介绍 1 1 概述 一条USB传输线分别由地线 电源线 D 和D 四条线构成 D 和D 是差分输入线 它
  • C ++ 数组

    目录 寻找最大 最小值 数组 寻找最大 最小值 对于这个编程测验 我们将找到用户将输入的15个数字的最小值和最大值以及平均值 include
  • FISCO BCOS Python SDK控制台使用

    控制台 常用命令 deploy 部署合约 console py deploy contract name save 参数包括 contract name 合约名 需要先放到contracts目录 save 若设置了save参数 表明会将合约
  • 如何优雅的搞定自动化测试的文件上传?

    在做web自动化时会碰到文件上传的实现功能 这个时候我们应当如何使用selenium进行文件的上传呢 一 标签类型为input 直接selenium直接上传 我们在做selenium自动化时 必须要面临的就是页面元素的定位 如果该上传文件功
  • 【车载以太网测试从入门到精通】——SOMEI/IP服务接口自动化测试(含CAPL源码)

    系列文章目录 车载以太网测试从入门到精通 系列文章目录汇总 文章目录 系列文章目录 前言 一 SOME IP介绍 1 SOME IP与车载以太网协议栈关系 2 SOME IP主要功能 3 SOME IP远程进程调用 RPC 二 SOME I
  • kvm在虚拟服务器,江博士带你轻松搞定KVM虚拟化

    阅读 3 609 在虚拟化中 必然存在宿主机与虚拟机通信的需要 一般来说 可以通过网桥的方式与虚拟机建立通信通道 通过网络传输数据 适用性比较强 但是 这种方式依赖于网络通畅 当虚拟机发生某些故障 网络已经发生问题 宿主机就需要另外一种方式
  • Java 多线程 --- 线程同步 显式锁ReentrantLock

    Java 多线程 线程同步 显式锁ReentrantLock 和 AQS机制 显式锁 Lock接口 显性锁的实现原理 AQS机制 显式锁 Lock接口 显式锁是自 JDK 1 5 开始引人的排他锁 作为一种线程同步机制 其作用与内部锁相 同
  • react与vue的区别

    React和Vue js是两个流行的JavaScript库 框架 用于构建用户界面 以下是React和Vue之间的一些主要区别 学习曲线 Vue js对于新手来说比React更容易学习和上手 构建方式 React强调组件的可重用性 而Vue
  • libevent HTTPserver/ libcurl HTTPClient构建

    22 1 前言 最近由于项目需要实现http客户端与http服务器 要求是httpServer httpClient模块独立 不能与用户代码耦合一起 因此为了使得httpserver及httpclient的通用性 在软件框架搭建中httpS
  • 利用SQLmap进行一次SQL注入

    题目 ctf show 题目博客很久之前已经做过一次了 很简单 这次只是用它来记录一下sqlmap如何使用 首先进入题目 是一个登录界面 猜测是sql注入 打开SQLmap 查询网站是否存在注入 burp抓包 将http头部文件复制到sql
  • python学习笔记十——模块与函数

    第五章 模块与函数 5 1 python程序的结构 函数 类 gt 模块 模块 模块 gt 包 函数 类 模块 包 Python python的程序由包 package 模块 module 和函数组成 模块是处理某一类问题的集合 模块由函数
  • Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库

    介绍 Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库 基于 ECMA 376 ISO IEC 29500 国际标准 可以使用它来读取 写入由 Microsoft Excel 2007 及以上版本创建的电
  • driverclasss oracle,Kettle 错误:Error connecting to database: (using class oracle.jdbc.driver.OracleDr...

    一 问题描述 使用kettle8 0连接oracle12c 发现将oracle安装目录下与ojdbc相关的jar包都拷贝到kettle的相关目录Program Files kettle data integration lib下 配置连接
  • 清华源镜像pip

    清华源镜像使用帮助 彻底解决timeout的问题 临时使用 pip install i https pypi tuna tsinghua edu cn simple some package opencv python 注意 末尾openc
  • Windows上提示 api-ms-win-core-path-l1-1-0.dll 丢失怎么办?

    Windows上提示 api ms win core path l1 1 0 dll 丢失怎么办 最近有用户在开启电脑的photoshop软件使用的时候 出现另外无法启动软件的情况 因为系统中缺失了对应的dll文件 那么这个情况怎么去进行问
  • 电源学习总结(三)——线性稳压的参数

    前文提到了一些线性稳压的主要特点 本文作者将结合NXP智能车大赛实际案例 说一下电源设计容易踩的坑和线性稳压选型过程 文章目录 主要参数 输出电压 输入电压 热阻 基准电压 压降 最大电流 输入电容 纹波抑制 选型实例 主要参数 在为实际应
  • 二十八.刷题.18

    输入三个字符 可以重复 后 按各字符的ASCII码从小到大的顺序输出这三个字符 include

随机推荐

  • 解密 QQ 号-队列-c语言

    问题描述 分析 每次从最前面拿两个 第 1 个扔掉 第 2 个 放到尾部 需要一个数组来存储这一串数即 int q 101 并初始化这个数组即 int q 101 0 6 3 1 7 5 8 9 2 4 head 用来记录队列的队首 即第一
  • 动态规划-货币问题

    动态规划 货币问题 题目一 arr是货币数组 其中的值都是正数 再给定一个正数aim 每个值都认为是一张货币 即便是值相同的货币也认为每一张都是不同的 返回组成aim的方法数 例如 arr 1 1 1 aim 2 第0个和第1个能组成2 第
  • 机器学习 之线性回归(包含推导过程)

    参考B站视频新手狂喜 目前B站最全最清晰的 机器学习算法 教程 从零开始详细解读 原理 代码实现 通通都在这里了 收藏慢慢学 决策树 随机森林 聚类分析 人工智能 哔哩哔哩 bilibili 线性回归 eg 银行贷款 数据 工资和年龄 特征
  • C Primer Plus(第六版)

    1 开发一个包含你需要的预处理器定义的头文件 写一个 h的头文件 包含你此次练习的题目就可以了 注意防止头文件被重复引用导致的错误 需要用 ifndef或者 pragma once 下面题目需要的声明和结构都在对应题目中 方法1 使用 if
  • mysql使用查询结果作为临时表

    一 select查询作为临时表 select cou name from select count AS cou enabled AS name from user group by enabled as a where cou gt 0
  • 基于微信小程序的新冠疫苗预约系统

    末尾获取源码 开发语言 Java Java开发工具 JDK1 8 后端框架 SSM 前端 微信小程序 Vue 数据库 MySQL5 7和Navicat管理工具结合 服务器 Tomcat8 5 开发软件 IDEA Eclipse 是否Mave
  • ArrayList集合及常用方法的使用

    ArrayLise
  • Spring IOC容器初始化过程 源码分析

    本文主要记录Spring容器创建 源码分析过程 首先贴上一张时序图 好久没画 忘的差不多了 画的不好 可以凑合看一下 接下来 贴上一份测试代码 这里使用AnnotationConfigApplicationContext来初始化Spring
  • upload-labs(11~12)通关笔记

    环境准备 1 php版本 lt 5 3 4 2 magic quotes gpc Off php我用的是upload labs官方推荐的5 2 17 搭建平台用的是phpStudy2018 修改magic quotes gpc magic
  • Java实现邮件发送功能

    确定发件人邮箱和密码 某些邮箱服务器为了增加邮箱本身密码的安全性 给 SMTP 客户端设置了独立密码 有的邮箱称为 授权码 对于开启了独立密码的邮箱 这里的邮箱密码必需使用这个独立密码 授权码 确认发件人邮箱的 SMTP 服务器地址 发件人
  • python人工智能:模型关系(泉舟时代)

    1 授课 林德尧 泉舟时代 未来城市技术总监 2 主要文章内容 通常下 Flask SQLAlchemy 的行为就像一个来自 declarative 扩展配置正确的 declarative 基类 因此 我们强烈建议您阅读 SQLAlchem
  • 《机器学习》Chapter 5 神经网络笔记

    机器学习 Chapter 5 神经网络 1 神经元模型 神经元接收到来自n个其它神经元传递过来的输入信号 这些输入信号通过带权重的连接进行传递 神经元接收到的总输入值将与神经元的阈值进行比较 然后通过激活函数处理以产生神经元的输出 2 感知
  • 使用css去除input边框

  • 不同CUDA版本对应的最小GPU运算能力和最低兼容驱动

    The minimum compute capability for various CUDA versions CUDA Version Minimum Compute Capability Default Compute Capabil
  • 使用 Python 进行游戏脚本编程 [翻译]

    翻译自 GDC 2002 上 Bruce Dawson 的 Game Scripting in Python 简单介绍 Python 作为游戏脚本语言的优势 原文 Game Scripting in Python 作者 Bruce Daws
  • 机器学习的环境搭建流程

    一 需要 python解释器 pycharm anaconda 机器学习需要的第三方包 二 流程 1 先确定进行机器学习需要的主要包之间的依赖关系及对应的python版本 建议python版本不要太高 3 6或者3 7比较好 因为许多第三方
  • 后端进阶之路——综述Spring Security认证,授权(一)

    前言 作者主页 雪碧有白泡泡 个人网站 雪碧的个人网站 推荐专栏 java一站式服务 前端炫酷代码分享 uniapp 从构建到提升 从0到英雄 vue成神之路 解决算法 一个专栏就够了 架构咱们从0说 数据流通的精妙之道 后端进阶之路 文章
  • 【华为OD机试真题 Python】最左侧冗余覆盖子串

    前言 本专栏将持续更新华为OD机试题目 并进行详细的分析与解答 包含完整的代码实现 希望可以帮助到正在努力的你 关于OD机试流程 面经 面试指导等 如有任何疑问 欢迎联系我 wechat steven moda email nansun09
  • 简单的编写一个通讯录并可进行增删改查功能

    改通讯录分为三个模块 test c contact c contact h 下面依次给我相应的代码 有想问的或者觉得有帮助的帮忙点个赞和关注一下哈 蟹蟹 主要用到了结构体指针来进行对结构体的修改查找之类的算法 test c define C
  • iOS 底层解析weak的实现原理

    参考地址 http www cocoachina com ios 20170328 18962 html weak 实现原理的概括 Runtime维护了一个weak表 用于存储指向某个对象的所有weak指针 weak表其实是一个hash 哈