【问题标题】:Strange behavior in Objective-C object dealloc in ARC enabled Environment在启用 ARC 的环境中,Objective-C 对象释放中的奇怪行为
【发布时间】:2013-01-11 21:35:48
【问题描述】:

问题:我看到在设置为 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,(它会设置静态实例变量 sInstancenil),人们会期望它的 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 { } 块中可以解决问题。

【问题讨论】:

  • 很难理解您的问题是什么。你能用正确的术语、完整的综合英语句子和一个实际编译的最小代码示例来改写它吗?
  • scenario2 和 output2 有什么遗漏吗?

标签: objective-c memory-management automatic-ref-counting


【解决方案1】:

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

场景#1:

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

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

场景 #2:

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

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

然后打印“清理后”。

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

因此现在打印“Dummy Dealloc”。

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


getInstance 由于 ARC 命名约定,返回未保留的返回值。如果您将您的方法重命名为copyInstance,那么它将返回一个 (+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.

【讨论】:

  • 马丁,这很好解释。你知道我可以在没有任何保留或自动释放的情况下获得指针的任何设置吗?查看了 attribute 设置,没有找到这样的选项..
  • @BharathBooshan:我在回答中添加了一些内容,希望对您有所帮助。
【解决方案2】:
Scenario #1

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

Output #1

 Dummy Dealloc
 After Cleanup

这有点直:

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

【讨论】:

  • Correct.. 但是首先添加一个语句'[BBSampleClass getInstance]',然后NSLog语句的顺序会改变。 [请注意,我已将类名从 BBDummyObject 更改为 BBSampleClass 以使其听起来更好]
猜你喜欢
  • 1970-01-01
  • 2012-08-27
  • 2015-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-13
相关资源
最近更新 更多