为什么这些 ARC 对象的行为不一致?

Why do these ARC objects behave inconsistently?

这是一个使用 ARC 的 OS X 的 Objective-C 程序 - 您可以使用 cc -fobjc-arc -o objc_arc_test objc_arc_test.m 或其他东西构建它。它创建了两对两个对象,一个使用 alloc/init ,另一个使用工厂函数(在 ARC 之前的代码中你会使用 autorelease 的那种东西),都在任何自动释放池之外,打印初始化和解除分配消息。

#import <Foundation/NSObject.h>
#include <stdio.h>

#if !__has_feature(objc_arc)
#error
#endif

@interface TestClass:NSObject {
    int value;
}
-(TestClass *)initWithValue:(int)value;
-(void)dealloc;
+(TestClass *)testClassWithValue:(int)value;
@end

@implementation TestClass
-(TestClass *)initWithValue:(int)value_ {
    if((self=[super init]))
        self->value=value_;

    printf("init: self=%p value=%d\n",self,self->value);
    return self;
}

-(void)dealloc {
    printf("dealloc: self=%p value=%d\n",self,self->value);
}

+(TestClass *)testClassWithValue:(int)value {
    return [[TestClass alloc] initWithValue:value];
}

@end

static void f() {
    TestClass *c5=[TestClass testClassWithValue:5];
    TestClass *c6=[[TestClass alloc] initWithValue:6];
}

static void f2() {
    TestClass *c7=[TestClass testClassWithValue:7];
    TestClass *c8=[[TestClass alloc] initWithValue:8];
}

int main() {
    f();
    f2();
}

我希望获得 4 个对象的初始化消息和 2 个对象的 dealloc 消息,因为 ARC 将确保 alloc+init 对象被销毁,并且由于缺少自动释放池,将离开其他的一个人。

但我得到的是 4 个对象的初始化消息和 3 个对象的 dealloc 消息:

init: self=0x7fea20500690 value=5
init: self=0x7fea205006f0 value=6
dealloc: self=0x7fea205006f0 value=6
init: self=0x7fea205006f0 value=7
init: self=0x7fea20500700 value=8
dealloc: self=0x7fea20500700 value=8
dealloc: self=0x7fea205006f0 value=7

我不明白这种行为!我希望 value=5 和 value=7 对象的行为相同。

为什么要这样做?

(OS X 10.11.6; Xcode 8 - Apple LLVM version 8.0.0 (clang-800.0.38), Target: x86_64-apple-darwin15.6.0, Thread model: posix)

因为我相信 OS X 10.9,所以在顶层自动创建了一个自动释放池,即使你没有创建(这消除了历史性的 "object autoreleased without an autorelease pool, just leaking" 警告) .

也就是说,这与这种情况并不特别相关。 ARC 不承诺任何东西都会自动发布。当它可以证明您以后不会使用该对象时,可以免费使用显式发布。 ARC 只是有义务保证强引用的对象不被销毁,没有强引用的对象被销毁。 Autoreleasepool 是 ARC 可以自由决定使用或不使用的实现细节。作为一种优化,ARC 通常会在我们过去手动使用它的地方避免使用自动释放池。

还值得注意的是,dealloc 从未真正承诺过。该程序无需 运行ning 即可自由终止(这是一个巨大的优化,这就是为什么 ObjC 程序可以比 C++ 程序终止得更快的原因)。在这种情况下它碰巧,但如果你依赖 dealloc 到 运行 或不依赖 运行,你可能误用了它。