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

时间:2018-09-15 19:34:17

标签: objective-c automatic-ref-counting

我正在刷新我在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行时,我也得到了预期的结果。有人可以解释吗?

1 个答案:

答案 0 :(得分:2)

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

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

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

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

实际上,我的期望是,即使您遇到这种情况,编译器最初也会发出该保留和释放,但是随后的遍历将删除多余的保留和释放。您的示例在保留之后立即发布了该版本,这使编译器更清楚地知道该对是多余的。因为保留被删除,所以自动发布优化不会启动,并且对您对象的引用确实会放入自动发布池中。

如果您的方法名为+newGAObject,则命名约定将意味着它返回+1引用,并且所有这些都将更改。 (当然,就目前情况而言,您的+create方法与内置+new方法的作用相同,除了必须添加ARC的自动发布。因此,您可以更改调用代码以使用+new,这也将回避此问题。)

我不知道为什么weakObject行很重要。但是,由于您看到的行为取决于某些优化,因此任何可以优化的东西都可以改变结果。