使用ARC进行不一致的对象释放?

Aky*_*Aky 8 objective-c automatic-ref-counting

我正在使用一个简单的命令行应用程序来处理内存(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;
}
Run Code Online (Sandbox Code Playgroud)

所以在上面,在分配了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;
}
Run Code Online (Sandbox Code Playgroud)

我期待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;

}
Run Code Online (Sandbox Code Playgroud)

最后一个代码就像第一个代码(使用归零的__weak指针); 唯一的区别是描述消息是在三个NSLog()调用的每一个中通过weakRef发送给对象的.但是这一次,即使在删除了两个强引用之后,对象也没有被释放(因为它仍然通过weakRef响应消息).

那么这里发生了什么?

mat*_*way 1

这看起来确实很奇怪。您对第二位代码的看法是正确的(在您的评论中),因为内存尚未被重新使用。但第三位代码比较奇怪。这是一个更简化的测试用例,显示了这个奇怪的问题:

#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;
    }
}
Run Code Online (Sandbox Code Playgroud)

注释掉该行后,输出为:

$ clang -fobjc-arc -framework Foundation test.m -o test -O3 && ./test
2012-02-12 00:39:42.769 test[6684:707] 0x0
Run Code Online (Sandbox Code Playgroud)

取消注释该行后,输出为:

$ clang -fobjc-arc -framework Foundation test.m -o test -O3 && ./test
2012-02-12 00:42:04.346 test[6688:707] 0x100f13f50
Run Code Online (Sandbox Code Playgroud)

这看起来非常奇怪,对我来说完全就像一个错误。我实际上不知道答案是什么,但我想我会把这个作为答案发布,以便让球滚动起来弄清楚发生了什么。

更新:

如果您在 构建此代码,O0那么似乎只有在没有调用weakRef时才将其归零。一次调用就意味着它不会被归零。foofoo