简体   繁体   English

与ARC的对象重新分配不一致?

[英]Inconsistent object deallocation with ARC?

I was playing around with memory (de)allocation stuff on a simple command line app for Mac OSX 10.7 built using Xcode Version 4.2.1 with ARC enabled, and the default build settings. 我在一个简单的命令行应用程序(用于Mac OSX 10.7)上使用内存(取消)分配的东西,该应用程序使用启用了ARC的Xcode版本4.2.1和默认的构建设置构建。 I can't explain the behaviour I'm getting from what I understand about ARC, so I'm hoping someone can explain what's going on here. 我无法从我对ARC的了解中得到解释的行为,所以我希望有人可以解释这里发生的事情。

First off, in the following code I'm getting the behaviour I expect (please note that the NLog() output is given in the comment after the corresponding statement) 首先,在下面的代码中,我得到了预期的行为(请注意,在相应语句之后的注释中给出了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;
}

So in the above, right after weakRef is assigned, the NSObject instance has two strong pointers to it, and therefore a retain count of 2. After zeroing objPtr1 there's still one retaining pointer to the instance, so it's still in memory and responds to the description message. 因此,在上面的方法中,在分配了weakRef之后,NSObject实例具有两个指向它的强指针,因此保留计数为2。将objPtr1归零后,仍然有一个指向该实例的保留指针,因此它仍在内存中并响应该实例。 说明消息。 After nil-ing objPtr2, there are no strong pointers to the object and it is deallocated (I'm assuming it is, since weakRef has been zeroed). 在使objPtr2无效之后,没有指向该对象的强指针,并且将其释放(我假设是,因为weakRef已被清零)。 So far, so good. 到目前为止,一切都很好。

Now, the same code with a small change: 现在,相同的代码稍有更改:

#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;
}

I was expecting weakRef to become a dangling pointer sending a message through which would cause the program to crash in the third NSLog() statement, but it seems the object instance is still alive and well. 我原以为weakRef会变成一个悬空指针,通过该消息发送一条消息,该消息将导致程序在第三条NSLog()语句中崩溃,但是看来该对象实例仍然有效。

Another thing I find weird: 我觉得很奇怪的另一件事:

#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;

}

This last code is like the first one (using the zeroed __weak pointer); 最后的代码就像第一个代码(使用零位__weak指针); the only difference is that the description message was sent to the object through weakRef in each of the three NSLog() calls. 唯一的不同是,描述消息是通过三个NSLog()调用中的每个均通过weakRef发送给对象的。 But this time round the object isn't deallocated even after the two strong references have been removed (since it's still responding to messages through weakRef). 但是这次,即使删除了两个强引用,对象也不会被释放(因为它仍然通过weakRef响应消息)。

So what's going on here? 那么这是怎么回事?

If you disassemble ARC-produced code, every access to a weak variable is wrapped in a call to this function: 如果反汇编ARC生成的代码,则对弱变量的每次访问都将包含在对此函数的调用中:

id objc_loadWeak(id *location)
{
    return objc_autorelease(objc_loadWeakRetained(location));
}

This checks if the object has already been dealloced, and if not, retains and autoreleases it, for extra safety against premature deallocs. 这将检查对象是否已经被释放,如果没有,则保留并自动释放该对象,以防止过早释放。

Therefore in your third example, the early calls to method on weakRef are causing its retain count to be increased, so nilling your pointers doesn't cause it to be dealloced. 因此,在您的第三个示例中,对弱引用方法的早期调用导致其保留计数增加,因此,将指针设置为零不会导致其被释放。

This does seem strange. 这似乎很奇怪。 You're right (in your comments) about the 2nd bit of code just being because the memory hasn't been re-used yet. 您对代码的第二位是正确的(只是在您的评论中),这是因为尚未重用内存。 But the 3rd bit of code is stranger. 但是第三段代码比较陌生。 Here is a more simplified test case which shows this strange problem: 这是一个更简化的测试用例,它显示了这个奇怪的问题:

#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;
    }
}

With that line commented out the output is: 注释掉该行后,输出为:

$ clang -fobjc-arc -framework Foundation test.m -o test -O3 && ./test
2012-02-12 00:39:42.769 test[6684:707] 0x0

With that line uncommented out the output is: 取消注释该行后,输出为:

$ clang -fobjc-arc -framework Foundation test.m -o test -O3 && ./test
2012-02-12 00:42:04.346 test[6688:707] 0x100f13f50

This seems deeply odd and looks entirely like a bug to me. 这似乎很奇怪,在我看来完全像个虫子。 I don't actually know what the answer is but thought I'd post this as an answer to get the ball rolling on figuring out what's going on. 我实际上不知道答案是什么,但我想将其发布为答案,以便弄清楚正在发生的事情。

Update: 更新:

If you build this at O0 then it seems that weakRef is zeroed only if there are no calls to foo . 如果您在O0处构建此weakRef则似乎只有在没有foo调用时, weakRef被清零。 A single call to foo will mean that it won't get zeroed. 一次调用foo将意味着它不会被清零。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM