使对象保持活动状态,直到后台任务完成

我试图实现一个方法,在后台执行一个任务,然后调用主线程上的一个块:

+ (void)migrateStoreWithCompletionHandler:(MigrationControllerCompletion)completion { MigrationController *controller = [[MigrationController alloc] initWithCompletionBlock:completion]; [controller migrateStore]; } 

这是-initWithCompletionBlock:方法:

 - (id)initWithCompletionBlock:(MigrationControllerCompletion)completion { self = [super init]; if (self) { _completion = [completion copy]; } return self; } 

后台工作发生在-migrateStore 。 问题是ARC在[controller migrateStore]之后释放controller 。 因为controller是保持在块上的对象,我不能永远称它。 有没有人有任何build议如何解决这个问题?

使用可怕的“保留周期”在你的青睐。

基本上,控制器对象强烈引用它的_completion iVar,所以如果你使这个块强烈引用self那么你有一个保留周期,这使得对象只要你想要的活着。

这些杂文暂时保留了保留周期警告。

在调用处理程序之后,可以通过将完成块设置为零来手动中断保留周期。

 - (id)initWithCompletionBlock:(MigrationControllerCompletion)completion { self = [super init]; if (self) { _completion = ^(BOOL success) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-retain-cycles" if (completion) completion(self, success); #pragma clang diagnostic pop _completion = nil; }; } return self; } 

然后,在你的代码中,当你想调用完成处理程序时,你不必传递self因为它已经在那里了…

 _completion(success); 

您可以考虑使用包含+migrateStoreWithCompletionHandler:的类+migrateStoreWithCompletionHandler:跟踪所有生成的MigrationController实例的私有数组或类似内容。 这将使controller不能被释放得太早,并允许你调用你的完成块。

您需要find一种方法稍后释放它们,以避免在制作MigrationControllers时慢慢增加内存使用量。 调用完成块后,可以考虑在-migrateStore的结尾处发布来自控制器的通知,然后让工厂类监听该通知并取消分配相应的控制器。 (如果你这么倾向,你也可以用一个委托模式来获得类似的行为。)

这是迄今为止我处理的ARC唯一真正的限制。 但是有一些简单的方法可以解决这个问题:

1)您可以为MigrationController对象创build一个静态variables,并在完成块被调用时将其设置nil

2) 只有当你真的知道你在做什么时才这样做!
直接使用CFRetain()CFRelease()

 + (void)migrateStoreWithCompletionHandler:(MigrationControllerCompletion)completion { MigrationController *controller = [[MigrationController alloc] initWithCompletionBlock:^(MigrationController *migrationController, BOOL finished, ...) { if (completion != nil) completion(migrationController, finished, ...); CFRelease((__bridge void *)migrationController); }]; [controller migrateStore]; // Make 'controller' live until the completion block is invoked CFRetain((__bridge void *)controller); }