iphone - 圆弧 : sending nil to an object doesn't call its dealloc immediately

标签 iphone ios objective-c automatic-ref-counting

我正在从手动内存管理过渡到 ARC,但遇到了问题。大多数时候,我通过在我的模型类中调用 performSelectorInBackground 来异步执行数据加载。问题是当模型收到 nil(发布)时,我需要停止任何模型代码的执行。在非 arc 中,一切都很简单——只要用户关闭窗口,它的 Controller 就会开始释放自身并释放它的模型 [_myModel release],因此模型停止它的代码执行(数据加载)并调用它的 dealloc 方法.

这在 ARC 中似乎有所不同。即使在从 Controller 收到 nil 消息后,模型仍然执行代码。它的 dealloc 方法仅在其代码执行(数据加载)后被调用。这是一个问题,因为当用户关闭窗口( Controller )时,代码执行应该尽快停止。这是对代码缺乏控制—— Controller 告诉模型——“走开,我不再需要你的工作了”,但模型仍然“正在努力完成它的工作”:)。

假设一个模型执行一些非常繁重的数据处理,持续时间为 10 秒。当用户打开窗口( Controller )时,模型开始进行处理。但是想象一个用户改变主意并关闭窗口,就在打开它之后。该模型仍然执行浪费处理。任何想法如何解决或解决这个问题?我不喜欢在我的模型中有一个特殊的 BOOL“shouldDealloc”属性并在 Controller dealloc 方法中设置为 YES 并在我的模型类条件中使用的想法。有更优雅的解决方案吗?

我做了一些演示项目来展示这个问题。对于测试,只需创建单 View 应用程序并粘贴代码。在 ViewController.xib 文件中创建按钮 - “开始计算”“停止计算”,并将它们的 IBActions 连接到 startCalculationPressed停止计算按下:

ViewController.h

#import "MyModel.h"

@interface ViewController : UIViewController <MyModelDelegate>

- (IBAction)startCalculationPressed:(id)sender;
- (IBAction)stopCalculationPressed:(id)sender;

@end

ViewController.m

@interface ViewController (){

  __strong MyModel *_myModel;
}
@end

@implementation ViewController

- (void)viewDidLoad
{
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning
{
  [super didReceiveMemoryWarning];
  // Dispose of any resources that can be recreated.
}

- (void)didCalculated
{
  NSLog(@"Did calculated...");
}

- (IBAction)startCalculationPressed:(id)sender
{
  NSLog(@"Starting to calculate...");

  _myModel = nil;
  _myModel = [[MyModel alloc] init];
  _myModel.delegate = self;

  [_myModel calculate];
}

- (IBAction)stopCalculationPressed:(id)sender
{
  NSLog(@"Stopping calculation...");
  _myModel.delegate = nil;
  _myModel = nil;
}
@end

向项目添加新的 MyModel 类:

MyModel.h

@protocol MyModelDelegate <NSObject>

  - (void)didCalculated;

@end

@interface MyModel : NSObject

  @property (nonatomic, weak) id<MyModelDelegate> delegate;

  - (void)calculate;

@end

MyModel.m

@implementation MyModel

- (void)dealloc
{
  NSLog(@"MyModel dealloc...");
}

- (void)calculate
{
  [self performSelectorInBackground:@selector(performCalculateAsync) withObject:nil];
}

- (void)performCalculateAsync
{
  // Performing some longer running task
  int i;
  int limit = 1000000;
  NSMutableArray *myList = [[NSMutableArray alloc] initWithCapacity:limit];

  for (i = 0; i < limit; i++) {

    [myList addObject:[NSString stringWithFormat:@"Object%d", i]];
  }

  [self performSelectorOnMainThread:@selector(calculateCallback) withObject:nil waitUntilDone:NO];

}

- (void)calculateCallback
{
  [self.delegate didCalculated];
}

@end

UPDATE Martin 是对的,performSelectorOnMainThread 总是保留 self,所以没有办法停止其他线程(ARC 和非 ARC 中)的代码执行,所以释放模型时不会立即调用 dealloc .因此,这应该通过条件检查使用适当的属性(例如委托(delegate))显式完成。

最佳答案

如果一个对象的释放计数下降到零,或者在 ARC 语言中,如果 对该对象的最后一个强引用消失了。

[self performSelectorInBackground:@selector(performCalculateAsync) withObject:nil];

添加对 self 的强引用,这解释了为什么对象没有被释放 在后台线程完成之前。

没有办法(据我所知)让后台线程“自动”停止。 这同样适用于以 dispatch_async()NSOperation 开头的 block 。 一旦启动,线程/ block /操作必须在点上监视某些属性 保存到哪里停止。

在您的示例中,您可以监控 self.delegate。如果它变为 nil,则没有人 对结果感兴趣,所以后台线程可以返回。在这种情况下, 将 delegate 属性声明为 atomic 是有意义的。

注意 self.delegate 也会自动设置为 nil 即使 stopCalculationPressed 没有被释放(因为它是一个弱属性) 被调用。

关于iphone - 圆弧 : sending nil to an object doesn't call its dealloc immediately,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15985617/

相关文章:

objective-c - Xcode 5 setter方法: use of undeclared identifier

ios - 如何在 iOS 11 中将导航栏设置为透明

iphone - 在将其上传到应用商店之前获取 iTunes 链接的 ID

ios - 获取安装应用程序的日期(iOS)

iphone - SnapChat 如何将数据发送到其他设备?

ios - Sprite 套件移动 Sprite

ios - 1Password Pods iOS 库未找到

iphone - 在 iOS 4.2 中对自定义类进行单元测试时出现 _OBJC_CLASS_$ 错误

iphone - iOS 应用程序安全的最佳实践

iphone - 有没有更快的方法来比较两个变量?