前言
关于 weak 的底层原理这篇文章 # iOS weak 底层实现原理(一) 以及后续的几篇文章已经讲的非常清楚了,大家可以跟着认真研究一番。
在第四篇文章 # iOS weak 底层实现原理(四):ARC 和 MRC 下 weak 变量的访问过程 中在介绍 objc_loadWeakRetained 时,并没给出对应的 Demo,我这里转发一个其它博客的介绍的,文末有参考链接。
__weak 对引用计数的影响
运行下面的代码:
@interface TestObj : NSObject
@property (nonatomic, copy) void(^block)(void);
- (void)testMethod;
@end
@implementation TestObj
- (void)dealloc {
NSLog(@"obj dealloc");
}
- (void)testMethod {
if (self.block) {
self.block();
}
NSLog(@"%@", self);
}
@end
// 在 ViewController 中
- (void)triggerWeakAction00 {
__block TestObj *testObj = [TestObj new];
testObj.block = ^{
testObj = nil;
};
[testObj testMethod];
}
- (void)triggerWeakAction01 {
__block TestObj *testObj = [TestObj new];
testObj.block = ^{
testObj = nil;
};
__weak typeof(testObj) weakObj = testObj;
[weakObj testMethod];
}
复制代码
大家觉得 triggerWeakAction00
和 triggerWeakAction01
调用之后会有什么问题?请测试一下!
你会发现 triggerWeakAction00
崩溃了,而 triggerWeakAction01
却没有,这是为什么?__weak
有什么神奇的作用呢?
下面是 triggerWeakAction01
的部分汇编代码:
0x105401e88 <+248>: movq -0x38(%rbp), %rax
0x105401e8c <+252>: movq 0x28(%rax), %rsi
0x105401e90 <+256>: leaq -0x80(%rbp), %rax
0x105401e94 <+260>: movq %rax, %rdi
0x105401e97 <+263>: movq %rax, -0x98(%rbp)
0x105401e9e <+270>: callq 0x10540268e ; symbol stub for: objc_initWeak
0x105401ea3 <+275>: movq -0x98(%rbp), %rdi
0x105401eaa <+282>: movq %rax, -0xa0(%rbp)
0x105401eb1 <+289>: callq 0x105402694 ; symbol stub for: objc_loadWeakRetained
0x105401eb6 <+294>: movq %rax, %rcx
0x105401eb9 <+297>: movq 0x31e0(%rip), %rsi ; "testMethod"
0x105401ec0 <+304>: movq 0x2151(%rip), %rdx ; (void *)0x00007fff20173780: objc_msgSend
0x105401ec7 <+311>: movq %rax, %rdi
0x105401eca <+314>: movq %rcx, -0xa8(%rbp)
0x105401ed1 <+321>: callq *%rdx
0x105401ed3 <+323>: jmp 0x105401ed8 ; <+328> at ViewController.m
0x105401ed8 <+328>: movq -0xa8(%rbp), %rax
0x105401edf <+335>: movq %rax, %rdi
0x105401ee2 <+338>: callq *0x2138(%rip) ; (void *)0x00007fff2018f760: objc_release
0x105401ee8 <+344>: leaq -0x80(%rbp), %rax
0x105401eec <+348>: movq %rax, %rdi
0x105401eef <+351>: callq 0x105402682 ; symbol stub for: objc_destroyWeak
复制代码
我们可以明显的看到 __weak
变量的生命周期:
- objc_initWeak (创建)
- objc_destroyWeak (销毁)
但是 objc_loadWeakRetained
是什么?于是查看了 obj4-750
的源码(版本有点老,但没关系):
/*
Once upon a time we eagerly cleared *location if we saw the object
was deallocating. This confuses code like NSPointerFunctions which
tries to pre-flight the raw storage and assumes if the storage is
zero then the weak system is done interfering. That is false: the
weak system is still going to check and clear the storage later.
This can cause objc_weak_error complaints and crashes.
So we now don't touch the storage until deallocation completes.
*/
id
objc_loadWeakRetained(id *location)
{
id obj;
id result;
Class cls;
SideTable *table;
retry:
// fixme std::atomic this load
obj = *location;
if (!obj) return nil;
if (obj->isTaggedPointer()) return obj;
table = &SideTables()[obj];
table->lock();
if (*location != obj) {
table->unlock();
goto retry;
}
result = obj;
cls = obj->ISA();
if (! cls->hasCustomRR()) {
// Fast case. We know +initialize is complete because
// default-RR can never be set before then.
ASSERT(cls->isInitialized());
if (! obj->rootTryRetain()) {
result = nil;
}
}
else {
// Slow case. We must check for +initialize and call it outside
// the lock if necessary in order to avoid deadlocks.
if (cls->isInitialized() || _thisThreadIsInitializingClass(cls)) {
BOOL (*tryRetain)(id, SEL) = (BOOL(*)(id, SEL))
class_getMethodImplementation(cls, @selector(retainWeakReference));
if ((IMP)tryRetain == _objc_msgForward) {
result = nil;
}
else if (! (*tryRetain)(obj, @selector(retainWeakReference))) {
result = nil;
}
}
else {
table->unlock();
class_initialize(cls, obj);
goto retry;
}
}
table->unlock();
return result;
}
复制代码
__weak
在发送消息之前对弱引用的对象进行 retain
操作,使用完成后再对对象进行 objc_release
操作。
到此我相信你已经弄明白了 triggerWeakAction00
崩溃了,而 triggerWeakAction01
却没有的原因!
所以我们多次调用 [weakObj testMethod];
呢?(可以自己尝试一下,并观察汇编)
参考
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END