【问题标题】:Weak property is set to nil in dealloc but property's ivar is not nil弱属性在 dealloc 中设置为 nil 但属性的 ivar 不是 nil
【发布时间】:2013-04-20 16:02:32
【问题描述】:

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

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

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

@implementation A
@end

第二类 B 实现了 dealloc

@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'

你知道为什么会这样吗?

【问题讨论】:

  • 当访问器方法知道应该清除弱引用时,可能会返回nil,但实例变量本身保持不变(并被释放,现在它是一个悬空指针)。
  • 我认为在 dealloc 中该对象仍然有效并且尚未被删除。例如,您可以从 NSNotificationCenter 注销它并可以访问其属性。
  • “你可以访问它”并不一定意味着它没有被释放,但我可能错了。

标签: objective-c automatic-ref-counting weak-references


【解决方案1】:

简答:实例变量a->_refObject-[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)。

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

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

原因是每次访问弱指针,ARC 编译器都会生成 致电objc_loadWeak()objc_loadWeakRetained()。来自documentation

id objc_loadWeakRetained(id *object)

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

所以即使a->refObject 在那个时候不为零,访问弱指针 通过objc_loadWeakRetained()(由属性访问器方法完成)返回零, 因为B对象的释放已经开始了。

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

【讨论】:

  • 不用了,谢谢。我认为每次访问弱指针时,编译器都会添加 objc_loadWeak 但似乎 a->_refObj 并非如此
  • @plamkata__:编译器为每次访问a->refObject添加objc_loadWeak[Retained],只有调试器直接访问ivar。
  • 非常有意义。我的印象是,当我观察到这种行为时,我在 dealloc 方法中使用 NSLog 来转储 ivar,这让我感到困惑。但实际上我没有使用 NSLog 而是使用调试器,所以正如你提到的它直接访问它。再次感谢您的帮助。
猜你喜欢
  • 2011-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多