在解除分配实例期间,ARC不会出现奇怪的异常行为

问题描述 投票:0回答:1

我在Objective-C世界中刷新了我的知识,现在我正在用__weak局部变量测试一些ARC。

我有非常简单的代码与这样的文件GAObject.h

#import <Foundation/Foundation.h>
@interface GAObject : NSObject
+ (instancetype)create;
@end

执行此接口GAObject.h

#import "GAObject.h"
@implementation GAObject
+ (instancetype)create {
    return [[GAObject alloc] init];
}
- (void)dealloc {
    NSLog(@"GAObject is being deallocated");
}
@end

所以有简单的工厂方法create和我重写dealloc方法来观察对象是否在我期待这个时被解除分配。现在有趣的部分main.m

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "Learning/GAObject.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {
        NSLog(@"1");
        NSObject *o1 = [[GAObject alloc] init];
        NSObject * __weak weakObject = o1; // Line 1
        o1 = nil; // o1 should be deallocated because there is no strong references pointing to o1.
        NSLog(@"2");
        NSObject *o2 = [GAObject create]; // Line 2
        o2 = nil; // o2 should be deallocated here too but it is not deallocated. Why?
        NSLog(@"3");

        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

在输出中我看到:

1
GAObject is being deallocated
2
3

但我期待的结果应该是:

1
GAObject is being deallocated
2
GAObject is being deallocated
3

如果我使用工厂方法创建o2然后我有这种行为。如果我像这样创建o2[[GAObject alloc] init]然后我得到预期的输出。另外我注意到当我用weakObject删除线时,我也得到预期的结果。有人可以解释一下吗?

objective-c automatic-ref-counting
1个回答
2
投票

这是因为ARC仍然尊重Cocoa内存管理命名约定。

根据这些约定,名为+create的方法返回+0引用。因此,在该方法的实现中,ARC必须通过自动释放参考来平衡alloc / init对的+1引用。

然后,在main()中,ARC必须假设它已经从+create的调用中获得了+0参考。如果它需要引用以在当前范围内存活,它将保留它,但它不会如此。当自动释放池耗尽时,第二个GAObject实例将被释放,但这将永远不会发生,因为UIApplicationMain()永远不会返回。如果您使用两个单独的自动释放池,一个用于处理GAObjects的代码,另一个用于调用UIApplicationMain(),我希望您能得到您期望的结果。

如果ARC确实需要引用存活,它将保留在强变量的赋值中,并在该变量被赋值为新值(包括nil)或超出范围时释放。 ARC具有运行时优化,其中被调用者中的自动释放返回和调用者中返回值的保留相互抵消,使得对象永远不会放入自动释放池中。如果发生这种情况,您将获得预期的结果。

事实上,我的期望是编译器最初会发出保留和释放,即使在你的情况下,但后续的传递删除了多余的保留和释放。您的示例在保留之后立即发布,这使得编译器更加明显该对是冗余的。因为保留被删除,所以自动释放优化不会启动,并且对对象的引用确实会被放入自动释放池中。

如果你的方法被命名为+newGAObject,那么命名约定意味着它返回一个+1引用,这一切都会改变。 (当然,就目前情况而言,你的+create方法与内置的+new方法完全相同,除了ARC必须添加的自动释放。所以,你可以改变调用代码来使用+new,那就是也回避了这个问题。)

我不知道为什么与weakObject的关系很重要。但是,由于您所看到的行为取决于某些优化,因此任何可以调整优化的内容都可以改变结果。

© www.soinside.com 2019 - 2024. All rights reserved.