【发布时间】:2012-02-28 16:42:00
【问题描述】:
我正在使用启用 ARC 的 Xcode 4.2.1 版和默认构建设置构建的 Mac OSX 10.7 的简单命令行应用程序上玩内存(取消)分配的东西。我无法解释从我对 ARC 的了解中得到的行为,所以我希望有人能解释这里发生了什么。
首先,在下面的代码中,我得到了我期望的行为(请注意,NLog() 输出在相应语句之后的注释中给出)
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSObject *objPtr1 = [[NSObject alloc] init];
NSObject *objPtr2 = objPtr1;
__weak NSObject *weakRef = objPtr1;
NSLog(@"%@", [objPtr1 description]); // <NSObject: 0x1001107d0>
objPtr1 = nil;
NSLog(@"%@", [objPtr2 description]); // <NSObject: 0x1001107d0>
objPtr2 = nil;
NSLog(@"%@", [weakRef description]); // (null)
return 0;
}
所以在上面,在分配了weakRef之后,NSObject实例有两个指向它的强指针,因此保留计数为2。在将objPtr1归零后,仍然有一个指向实例的保留指针,所以它仍然在内存中并且响应描述消息。在 nil-ing objPtr2 之后,没有指向该对象的强指针并且它被释放(我假设它是,因为 weakRef 已被归零)。到目前为止,一切顺利。
现在,相同的代码稍作改动:
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSObject *objPtr1 = [[NSObject alloc] init];
NSObject *objPtr2 = objPtr1;
__unsafe_unretained NSObject *weakRef = objPtr1; // __unsafe_unretained instead of just __weak
NSLog(@"%@", [objPtr1 description]); // <NSObject: 0x1001107d0>
objPtr1 = nil;
NSLog(@"%@", [objPtr2 description]); // <NSObject: 0x1001107d0>
objPtr2 = nil;
NSLog(@"%@", [weakRef description]); // <NSObject: 0x1001107d0>
//why was the object instance not deallocated and the preceding statement not crash the program?
return 0;
}
我原以为weakRef会变成一个悬空指针,通过它发送一条消息会导致程序在第三个NSLog()语句中崩溃,但似乎对象实例还活着并且很好。
我觉得奇怪的另一件事:
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSObject *objPtr1 = [[NSObject alloc] init];
NSObject *objPtr2 = objPtr1;
__weak NSObject *weakRef = objPtr1; // __weak again
NSLog(@"%@", [weakRef description]); // <NSObject: 0x1001107d0>
objPtr1 = nil;
NSLog(@"%@", [weakRef description]); // <NSObject: 0x1001107d0>
objPtr2 = nil;
NSLog(@"%@", [weakRef description]); // <NSObject: 0x1001107d0>
return 0;
}
最后一个代码和第一个代码一样(使用归零的 __weak 指针);唯一的区别是在三个 NSLog() 调用中的每一个中,描述消息都是通过weakRef 发送到对象的。但是这一次,即使删除了两个强引用,对象也没有被释放(因为它仍然通过weakRef响应消息)。
那么这里发生了什么?
【问题讨论】:
-
好的,如果我在“objPtr2 = nil”和“NSLog(@ "%@", [weakRef description])" 程序有时会崩溃(出现 EXC_BAD_EXCESS 错误),有时不会。如果我插入另一个填充 NSLog() 语句,那么程序肯定会崩溃(至少在我运行它的所有时间)。所以我想这意味着回收释放的内存不一定是对象丢失所有强引用后发生的绝对第一件事。
-
代码 #3 对我来说仍然是一个谜,但是......如果我通过代码中的 weakRef 向我们的实例发送消息,那么即使在所有对它的强引用被删除了——甚至当 main() 返回时也没有(我将 NSObject 子类化并覆盖了 dealloc 方法,放入 NSLog() 以检查它是否会被调用——它不是)。似乎它可能是一个错误。
-
这确实很奇怪,是的。你对代码 #2 是正确的 - 内存还没有被重新使用,所以它仍然指向一个“有效的”
NSObject,所以description工作正常。代码#3 很奇怪。我注意到如果将第二个description发送到weakRef,它不会为零weakRef。即,如果您将description发送到weakRef,然后发送objPtr2,然后发送weakRef,它会正确输出最后一个(null)。 -
事实上,您只需要在看起来的对象上调用 2 个方法。大概这里有某种错误。我没有时间再打猎,但我会在其他时间进一步看看。一个有趣的!
-
刚刚添加了一个更精简版本的代码 3 的答案,我认为它显示得更好一些。我认为这是一个错误,所以我们可能应该提交雷达。
标签: objective-c automatic-ref-counting