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 に答える