1

问题:我看到在设置为 nil 后在静态对象上调用 dealloc 方法的顺序中出现了一些奇怪的行为。

为了说明问题,我取了一个类名BBSampleClass,它有两个方法

interface BBSampleClass : NSObject
+ (BBDummyObject*)getInstance; 
+ (void)cleanUp;
@end


static BBSampleClass *sInstance = nil;
@implementation BBSampleClass

+ (void)initialize
{
    sInstance = [[BBDummyObject alloc] init];
}

+ (BBDummyObject*)getInstance
{
    return sInstance;
}

+ (void)cleanUp
{
    sInstance = nil;
}

- (void)dealloc
{
    NSLog(@"BBSampleClass Object Dealloc");
}

@end

在我的应用程序的某个时刻,如果我在BBSampleClass中调用类方法cleanUp(它将静态实例变量sInstance设置为nil),人们会期望在执行其他语句之前立即调用它的 dealloc,因为此时没有其他对象拥有 sInstance .

即执行这两个语句

[BBSampleClass cleanUp]
NSLog(@"After Cleanup");

应该将以下内容输出到控制台,这是正确的。

**2013-01-11 14:14:32.280 BBSampleCode[7781:c07] BBSampleClass Object Dealloc

2013-01-11 14:14:32.280 BBSampleCode[7781:c07] 清理后**

但是,如果我尝试通过它的getInstance类方法检索 BBSampleClass 的对象,就像这样

[BBSampleClass getInstance];//do something with object 
[BBSampleClass cleanUp];  
NSLog(@"After Cleanup");

NSLog 语句的执行顺序颠倒了,即 BBSampleClass 的静态对象在执行完语句NSLog(@"After Cleanup")后被释放,这是错误的。

 **2013-01-11 14:15:43.940 BBWebView[7811:c07] After Cleanup
2013-01-11 14:15:43.940 BBWebView[7811:c07] BBSampleClass Object Dealloc**

编辑:解决方案

将 [BBSampleClass getInstance] 移动到 @autoreleasepool { } 块中可以解决问题。

4

2 回答 2

2

initialize在任何其他消息之前发送到一个类,因此您从对sTop对象的强引用开始。

场景#1:

[BBDummyObject cleanUp]sTop = nil。对该对象的最后一个强引用消失了,该对象立即被释放。

所以“Dummy Dealloc”在“After Cleanup”之前打印。

场景#2:

[BBDummyObject getInstance]方法保留返回的对象sTop并将其添加到自动释放池中,如Clang ARC 文档的3.2.3 未保留的返回值中所述。

[BBDummyObject cleanUp]sTop = nil。这会破坏对对象的强引用,sTop并且保留计数会减少。但是保留计数仍然是正数,因此此时对象没有被销毁。

然后打印“清理后”。

当程序离开 current 的范围时@autoreleasepool { },自动释放池中的所有对象都会收到一条release消息。您的对象的保留计数现在为零,并且该对象已被释放。

因此现在打印“Dummy Dealloc”。

一个简化的描述是:[BBDummyObject getInstance]创建一个强引用,当当前自动释放池被销毁时自动销毁。因此 [BBDummyObject cleanUp]不会立即销毁对象,而是在“Dummy Dealloc”之前打印“After Cleanup”。


getInstance由于 ARC 命名约定,返回未保留的返回值。如果您将方法重命名为copyInstancethen ,它将返回一个 (+1) 保留值,该值不会添加到自动释放池中。例如:

BBDummyObject *obj = [BBDummyObject copyInstance]; // retain count of object is increased.
// do something with obj ...
obj = nil; // retain count is decreased immediately.
[BBDummyObject cleanUp]; // should dealloc object immediately.
于 2013-01-11T22:11:41.707 回答
1
Scenario #1

[BBDummyObject cleanUp];
NSLog(@"After Cleanup");

Output #1

 Dummy Dealloc
 After Cleanup

这有点直:

您调用了 cleanUp,将其设置为nil. 调用哪个结果dealloc。然后你的NSLog工作。

于 2013-01-11T21:50:33.027 回答