4

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

+ (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. 问题是 ARCcontroller[controller migrateStore]. 因为controller是持有块的对象,所以我无法调用它。有人对如何解决此问题有任何建议吗?

4

3 回答 3

4

使用对你有利的可怕的“保留周期”。

基本上,控制器对象强烈引用它的_completion iVar,所以如果你使那个块强烈引用,self那么你就有一个保留周期,只要你想要,它就会让对象保持活动状态。

编译指示暂时使保留周期警告静音。

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

- (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);
于 2012-08-31T18:25:48.413 回答
2

您可以考虑让包含+migrateStoreWithCompletionHandler:跟踪私有数组或类似数组中所有生成的 MigrationController 实例的类。这样可以避免controller过早释放,并允许您调用完成块。

但是,您需要找到一种稍后释放它们的方法,以避免在制作 MigrationControllers 时缓慢增加内存使用量。您可能会考虑在-migrateStore调用完成块之后从控制器发布通知,然后让您的工厂类监听该通知并取消分配适当的控制器。(如果您愿意,您也可以使用委托模式获得类似的行为。)

于 2012-08-31T16:14:26.340 回答
1

这是迄今为止我处理的 ARC 唯一真正的限制。不过,有一些简单的方法可以解决它:

MigrationController1)您可以为对象创建一个静态变量,并将其设置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);
}
于 2012-08-31T16:26:41.333 回答