2016-12-10 8 views
0

私は私の解放のアプリから、次のクラッシュレポートを持っている:コアデータの同時実行 `performBlockAndWait:` NSManagedObjectContextゾンビ

enter image description here

synchronizeMyWords方法は、最終的には、データベースからエンティティをフェッチメインのコンテキスト親とプライベートキュー・コンテキストを作成し、結果を保存します。すべての操作はバックグラウンドスレッドにあります。このメソッドは、アプリがbackgroundと​​になるたびに呼び出されます。あなたは、私はコアデータ・スタックを管理するためにMagical Recordサードパーティのライブラリを使用していますご覧のよう

- (AWSTask *)synchronizeMyWords { 
    __weak typeof(self) weakSelf = self; 

    AWSContinuationBlock block = ^id _Nullable(AWSTask * _Nonnull task) { 
    if ([task.result isKindOfClass:[NSArray class]]) { 
     NSArray * records = (NSArray *)task.result; 
     NSManagedObjectContext * context = [NSManagedObjectContext MR_contextWithParent:[NSManagedObjectContext MR_defaultContext]]; 
     [context performBlockAndWait:^{ 
     for (NSDictionary * info in records) { 
      [RDRWord MR_createEntityInContext:context]; 
     } 

     [context save:nil]; 
     }]; 
     return [AWSTask taskWithResult:@YES]; 
    } 
    return [AWSTask taskWithError:[NSError errorWithDomain:@"" code:404 userInfo:nil]]; 
    }; 

    AWSExecutor * executor = [AWSExecutor defaultExecutor]; 


    return [[self loadLocalWords] continueWithExecutor:executor withBlock:block]; 
} 

:ここでは簡略化された方法です。

+ (NSManagedObjectContext *) MR_contextWithParent:(NSManagedObjectContext *)parentContext 
{ 
    NSManagedObjectContext *context = [self MR_newPrivateQueueContext]; 
    [context setParentContext:parentContext]; 
    [context MR_obtainPermanentIDsBeforeSaving]; 
    return context; 
} 

あなたはgithubのhereに全体NSManagedObjectContext+MagicalRecordカテゴリを確認することができます。ここではプライベートキュー・コンテキストを作成する方法です。

contextのオブジェクトは、performBlockAndWait:のオブジェクトがスコープをエスケープする前に解放されていますか? 私は個人的にクラッシュを再現できませんが、多くのユーザー(iOS 8.1-10デバイス)がこの問題の影響を受けています。

UPDATE 1:

ここでは、インスタンス同じレポートですblog

答えて

1

にCore Dataは、バックグラウンドスレッドに対処するために十分なAPIを提供します。これらはMagical Recordからもアクセスできます。

スレッドが不必要に多く作成されているように見えます。私はAWSContinuationBlockAWSExecutorの雇用は良い考えではないと思います。 synchronizeMyWordsは、バックグラウンドスレッドから呼び出すことができます。ブロックはバックグラウンドスレッド上で実行される可能性があります。ブロック内で、子コンテキストにリンクされた新しい背景スレッドを作成します。 loadLocalWordsが何を返すか、またはcontinueWithExecutor:block:がスレッドをどのように扱うかははっきりしない。

また、データの保存に問題があります。メインコンテキストは、子コンテキストが保存された後は保存されません。おそらくこれは後で起こりますが、他の操作と関連している可能性があります。そのため、コードが以前に動作していたという事実は、おそらくは「間違っている」でしょう。

スレッドコードを簡略化することをお勧めします。コアデータブロックAPIに自分自身を限定する必要があります。

+0

正しいメインコンテキストは保存されません。アプリがバックグラウンドになると主なコンテキストが保存されます。それは別の操作です。 'continueWithExecutor:block:'は、 'continuationBlock'がバックグラウンドスレッドで実行されることを意味します。ここでのタスクはリンクの簡単な説明を介して[BFTask](https://github.com/BoltsFramework/Bolts-ObjC)サブクラスです。 –

1

@Mundiの答えは、あなたが従うべき一般的なアプローチを書いたので、正しいとマークしました。今、私はそれをどのようにデバッグしたかをここで共有したいと思います。 まず、xcodeのデバッグ同時実行アサーションをオンにすることができることを知りました。 あなたが起動時に次の引数を渡す必要があります:私はそれを回したら

2016-12-12 01:58:31.665 your-app[4267:2180376] CoreData: annotation: Core Data multi-threading assertions enabled.

:アプリケーションの出力で、

-com.apple.CoreData.ConcurrencyDebug 1

enter image description here

さて、あなたはメッセージをログ表示されるはずですon、私のアプリはsynchronizeMyWordsの方法でクラッシュしました(正直なところ、そこだけではありません。デバッグモードではデフォルトでertions?)。私はdefaultExecutorAWSCore libraryにあるものにチェックして、この見た:

+ (instancetype)defaultExecutor { 
    static AWSExecutor *defaultExecutor = NULL; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
     defaultExecutor = [self executorWithBlock:^void(void(^block)()) { 
      // We prefer to run everything possible immediately, so that there is callstack information 
      // when debugging. However, we don't want the stack to get too deep, so if the remaining stack space 
      // is less than 10% of the total space, we dispatch to another GCD queue. 
      size_t totalStackSize = 0; 
      size_t remainingStackSize = remaining_stack_size(&totalStackSize); 

      if (remainingStackSize < (totalStackSize/10)) { 
       dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block); 
      } else { 
       @autoreleasepool { 
        block(); 
       } 
      } 
     }]; 
    }); 
    return defaultExecutor; 
} 

そのif声明によると、私はcontinuationBlockDISPATCH_QUEUE_PRIORITY_DEFAULTキュー上で実行されることは保証されませんでした。だから、共有された1つのdispatch_queue_tキューを作成し、すべての操作をperformBlockAndWait: CoreDataメソッドと組み合わせて呼び出します。その結果、現在クラッシュはなく、私は新しいリリースを提出しました。私はcontextゾンビでクラッシュレポートを取得しなければ、この投稿を更新します。

+0

共有ありがとう!これは他の人には便利だと思います。 – Mundi

関連する問題