runtime 对 __weak 弱引用处理方式
切入主题,这里笔者使用的 runtime 版本为 objc4-680.tar.gz。 我在入口文件 main.m 中加入如下代码:
1 2 3 4 5 6 7 8 |
int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... NSObject *p = [[NSObject alloc] init]; __weak NSObject *p1 = p; } return 0; } |
单步运行,发现会跳入 NSObject.mm
中的 objc_initWeak()
这个方法。在进行编译过程前,clang 其实对 __weak 做了转换,将声明方式做出了如下调整。
1 |
NSObject objc_initWeak(&p, 对象指针); |
其中的对象指针,就是代码中的 [[NSObject alloc] init]
,而 p 是我们传入的一个弱引用指针。而对于 objc_initWeak()
方法的实现,在 runtime 中的源码如下:
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 中的缓存判断),这里判断了其指针指向的类对象是否有效,无效直接释放,不再往深层调用函数。
需要注意的是,当修改弱引用的变量时,这个方法非线程安全。所以切记选择竞争带来的一些问题。
继续阅读 objc_storeWeak()
的实现:
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
// HaveOld: true - 变量有值 // false - 需要被及时清理,当前值可能为 nil // HaveNew: true - 需要被分配的新值,当前值可能为 nil // false - 不需要分配新值 // CrashIfDeallocating: true - 说明 newObj 已经释放或者 newObj 不支持弱引用,该过程需要暂停 // false - 用 nil 替代存储 template bool HaveOld, bool HaveNew, bool CrashIfDeallocating> static id storeWeak(id *location, objc_object *newObj) { // 该过程用来更新弱引用指针的指向 // 初始化 previouslyInitializedClass 指针 Class previouslyInitializedClass = nil; id oldObj; // 声明两个 SideTable // ① 新旧散列创建 SideTable *oldTable; SideTable *newTable; |