我正在使用一个简单的命令行应用程序来处理内存(de)分配内容,该应用程序适用于使用启用了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归零后,仍有一个指向实例的保留指针,所以它仍然在内存中响应描述消息。在nir-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响应消息)。
那么这里发生了什么?
答案 0 :(得分:3)
如果您反汇编A.R.C.生成的代码,每次访问弱变量都会包含在对此函数的调用中:
id objc_loadWeak(id *location)
{
return objc_autorelease(objc_loadWeakRetained(location));
}
检查对象是否已被解除分配,如果没有,则保留并自动释放它,以防止过早的deallocs。
因此,在你的第三个例子中,对weakRef方法的早期调用会导致其保留计数增加,因此使指针无效会导致它被解除分配。
答案 1 :(得分:1)
这看起来很奇怪。你(在你的评论中)关于代码的第二位是正确的,因为内存还没有被重用。但第3位代码更奇怪。这是一个更简化的测试用例,它显示了这个奇怪的问题:
#import <Foundation/Foundation.h>
@interface SomeClass : NSObject
@end
@implementation SomeClass
- (void)foo {
}
@end
int main (int argc, const char * argv[]) {
@autoreleasepool {
SomeClass *objPtr1 = [[SomeClass alloc] init];
__weak SomeClass *weakRef = objPtr1;
// [weakRef foo];
[weakRef foo];
objPtr1 = nil;
NSLog(@"%p", weakRef);
return 0;
}
}
在该行注释掉输出后:
$ clang -fobjc-arc -framework Foundation test.m -o test -O3 && ./test
2012-02-12 00:39:42.769 test[6684:707] 0x0
如果该行未注释,则输出为:
$ clang -fobjc-arc -framework Foundation test.m -o test -O3 && ./test
2012-02-12 00:42:04.346 test[6688:707] 0x100f13f50
这看起来很奇怪,对我来说看起来完全像个错误。我实际上并不知道答案是什么,但我认为我会发布这个答案,以便弄清楚究竟发生了什么。
<强>更新强>
如果您在O0
构建此代码,则只有在{em>没有调用weakRef
时,foo
似乎才会归零。单次调用foo
将意味着它不会被归零。