13

我在启用 ARC 的 Objective-C 中注意到以下内容:

让我们有简单的 A 类和自动合成的弱属性

@interface A
@property (nonatomic, weak) id refObject;
@end

@implementation A
@end

和实现了dealloc的第二类B

@interface B
@end

@implementation B
-(void) dealloc
{
    NSLog(@"In dealloc");
}
@end

最后在 A 类的某个地方有以下内容:

@implementation A
...
-(void) foo
{
   B* b = [B new];
   self.refObject = b;
   // Just use b after the weak assignment
   // in order to not dealloc 'b' before assignement 
   NSLog(@"%@", b);
}
...
@end 

如果我在其中设置断点[B dealloc]并检查[A refObject]属性,我可以看到它a.refObject是 nil 但a->_refObject不是 nil 并指向'b'

任何想法为什么会发生这种情况?

4

1 回答 1

25

简短的回答:实例变量a->_refObject(还)不是 nil in -[B dealloc],但是每次访问该弱指针都是通过一个 ARC 运行时函数完成的,如果释放已经开始,则返回 nil 。

长答案:通过设置观察点,您可以看到a->_refObject在解除分配过程结束时将其设置为 nil。堆栈回溯(当观察点被命中时)如下所示:

frame #0: 0x00007fff8ab9f0f8 libobjc.A.dylib`arr_clear_deallocating + 83
frame #1: 0x00007fff8ab889ee libobjc.A.dylib`objc_clear_deallocating + 151
frame #2: 0x00007fff8ab88940 libobjc.A.dylib`objc_destructInstance + 121
frame #3: 0x00007fff8ab88fa0 libobjc.A.dylib`object_dispose + 22
frame #4: 0x0000000100000b27 weakdealloc`-[B dealloc](self=0x000000010010a640, _cmd=0x00007fff887f807b) + 151 at main.m:28
frame #5: 0x0000000100000bbc weakdealloc`-[A foo](self=0x0000000100108290, _cmd=0x0000000100000e6f) + 140 at main.m:41
frame #6: 0x0000000100000cf5 weakdealloc`main(argc=1, argv=0x00007fff5fbff968) + 117 at main.m:52
frame #7: 0x00007fff8c0987e1 libdyld.dylib`start + 1

并被object_dispose()调用-[NSObject dealloc](可以在 http://www.opensource.apple.com/source/objc4/objc4-532/runtime/NSObject.mm中看到)。

因此 in -[B dealloc],在调用a->_refObject(编译器生成)之前不是 nil 。[super dealloc]

所以问题仍然存在:为什么a.refObject此时返回 nil ?

原因是每次访问弱指针,ARC 编译器都会调用objc_loadWeak()or objc_loadWeakRetained()。从文档中:

id objc_loadWeakRetained(id *object)

如果 object 注册为 __weak 对象,并且存储在 object 中的最后一个值尚未被释放或开始释放,则保留该值并返回它。否则 > 返回 null。

因此,即使a->refObject此时不是 nil,通过objc_loadWeakRetained()(通过属性访问器方法完成)访问弱指针也会返回 nil,因为B对象的释放已经开始。

调试器a->refObject直接访问,不调用objc_loadWeak().

于 2013-04-20T18:44:08.727 回答