保留对象的生命,直到后台任务完成(Keep object alive until a backgro

2019-06-27 15:53发布

我想实现的是在后台执行任务的方法,然后调用主线程块:

+ (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之后[controller migrateStore] 由于controller是保存到块的对象,我不能够永远调用它。 有没有人对如何解决这个问题有什么建议?

Answer 1:

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

基本上,控制器对象强烈引用其_completion伊娃,所以如果你把该块强烈引用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);


Answer 2:

你可以考虑具有含类+migrateStoreWithCompletionHandler:跟踪所有生成的MigrationController实例的私人阵列或相似。 这将保持controller被过早地释放,并允许你打电话给你完成块。

你需要找到一种方法后来将他们释放,不过,为了避免缓慢增长你的内存使用情况为您做出MigrationControllers。 你可能会考虑在年底发布来自控制器的通知-migrateStore调用完成块之后,然后让你的工厂类侦听该通知和解除分配适当的控制器。 (你也可以得到类似的行为与委托模式,如果你是这样的倾向。)



Answer 3:

这是ARC的唯一真正的限制,我到目前为止已经处理了。 有简单的方法来解决它,但是:

1)可以创建用于一个静态变量MigrationController对象,并将其设置为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);
}


文章来源: Keep object alive until a background task finishes