我在一个简单的 Mac OSX 10.7 命令行应用程序上玩内存(取消)分配的东西,使用 Xcode 版本 4.2.1 启用 ARC 和默认build设置。我无法根据我对 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 响应消息)。
那么这里发生了什么?
最佳答案
如果反汇编 A.R.C. 生成的代码,对弱变量的每次访问都包含在对此函数的调用中:
id objc_loadWeak(id *location)
{
return objc_autorelease(objc_loadWeakRetained(location));
}
这会检查对象是否已经被释放,如果没有,则保留并自动释放它,以防止过早释放。
因此,在您的第三个示例中,对 weakRef 方法的早期调用导致其保留计数增加,因此将您的指针置零不会导致它被释放。
关于objective-c - 与 ARC 不一致的对象释放?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9173120/