今年年底做了很多决定, 离开工作三年的深圳, 来到了上海, 发现深圳和上海在苹果这方面还是差距有点大的, 上海的市场 8 成使用 swift 编程, 而深圳 8 成的使用 OC, 这点还是比较让准备来上海打拼的苹果工程师有点小压力的. 毕竟以后苹果还是 swift 使用的多, 现在已经 swift4.x 了, 所以早点接触, 还是有优势的, 不过. 咱们闲话少说, 今天我们将继续讲述 OC 修饰属性的一个 Weak 修饰符的底层实现, 有时间我会花时间讲述 swift4.x.
一, weak 基本用法
weak 是弱引用, 用 weak 来修饰, 描述所引用对象的计数器并不会加 1, 而且 weak 会在引用对象被释放的时候自动置为 nil, 这也就避免了野指针访问坏内存而引起奔溃的情况, 另外 weak 也可以解决循环引用.
拓展: 为什么修饰代理使用 weak 而不是用 assign?
assign 可用来修饰基本数据类型, 也可修饰 OC 的对象, 但如果用 assign 修饰对象类型指向的是一个强指针, 当指向的这个指针释放之后, 它仍指向这块内存, 必须要手动给置为 nil, 否则会产生野指针, 如果还通过此指针操作那块内存, 会导致 EXC_BAD_ACCESS 错误, 调用了已经被释放的内存空间; 而 weak 只能用来修饰 OC 对象, 而且相比 assign 比较安全, 如果指向的对象消失了, 那么它会自动置为 nil, 不会导致野指针.
二, weak 原理概括
weak 表其实是一个哈希表, key 是所指对象的指针, value 是 weak 指针的地址数组.(value 是数组的原因是: 因为一个对象可能被多个弱引用指针指向)
Runtime 维护了一张 weak 表, 用来存储某个对象的所有的 weak 指针.
weak 原理实现过程三步骤
初始化开始时, 会调用 objc_initWeak 函数, 初始化新的 weak 指针指向对象的地址
2. 紧接着, objc_initWeak 函数里面会调用 objc_storeWeak() 函数, objc_storeWeak() 函数的作用是用来更新指针的指向, 创建弱引用表.
3. 在最后会调用 clearDeallocating 函数. 而 clearDeallocating 函数首先根据对象的地址获取 weak 指针地址的数组, 然后紧接着遍历这个数组, 将其中的数组开始置为 nil, 把这个 entry 从 weak 表中删除, 最后一步清理对象的记录.
拓展: 详细步骤
初始化开始时, 会调用 objc_initWeak 函数, 初始化新的 weak 指针指向对象的地址
当我们初始化 weak 变量时, runtime 会调用 NSObject.mm 中的 objc_initWeak, 而 objc_initWeak 函数里面的实现如下:
- 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_initWeak() 函数首先判断指针指向的类对象是否有效, 无效, 直接返回; 否则通过 storeWeak() 被注册为一个指向 value 的_weak 对象
2. objc_initWeak 函数里面会调用 objc_storeWeak() 函数, objc_storeWeak() 函数的作用是用来更新指针的指向, 创建弱引用表.
3.. 在最后会调用 clearDeallocating 函数. 而 clearDeallocating 函数首先根据对象的地址获取 weak 指针地址的数组, 然后紧接着遍历这个数组, 将其中的数组开始置为 nil, 把这个 entry 从 weak 表中删除, 最后一步清理对象的记录.
问: 当 weak 指向的对象被释放时, 如何让 weak 指针置为 nil 的呢?
1, 调用 objc_release
2, 因为对象的引用计数为 0, 所以执行 dealloc
3, 在 dealloc 中, 调用了_objc_rootDealloc 函数
4, 在_objc_rootDealloc 中, 调用了 object_dispose 函数
5, 调用 objc_destructInstance
6, 最后调用 objc_clear_deallocating, 详细过程如下:
a. 从 weak 表中获取废弃对象的地址为键值的记录
b. 将包含在记录中的所有附有 weak 修饰符变量的地址, 赋值为 nil
c. 将 weak 表中该记录删除
d. 从引用计数表中删除废弃对象的地址为键值的记录
本文讲述了 weak 底层实现原理, 也是面试经常被问到的一点, 希望对大家有所帮助, 谢谢!
来源: https://www.cnblogs.com/guohai-stronger/p/10161870.html