0

リリースしたアプリから次のクラッシュ レポートがあります。

ここに画像の説明を入力

synchronizeMyWordsメソッドはデータベースからエンティティをフェッチし、メイン コンテキストの親でプライベート キュー コンテキストを作成し、最後に結果を保存します。すべての操作はバックグラウンド スレッドで行われます。backgroundこのメソッドは、アプリがおよびに入るたびに呼び出されforegroundます。簡単な方法を次に示します。

- (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];
}

ご覧のとおり、Core Data スタックを管理するためにMagical Recordサードパーティ ライブラリを使用しています。プライベート キュー コンテキストを作成する方法を次に示します。

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

ここNSManagedObjectContext+MagicalRecordで githubのカテゴリ全体を確認できます。

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

更新 1:

これは、たとえばブログの同じレポートです

4

2 に答える 2

2

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

-com.apple.CoreData.ConcurrencyDebug 1

ここに画像の説明を入力

これで、アプリケーションの出力に次のログ メッセージが表示されます。

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

オンにすると、アプリがsynchronizeMyWordsメソッドでクラッシュしました (正直なところ、そこだけではありません。不思議なことに、Apple はデバッグ モードでデフォルトで同時実行性アサーションを含めないのはなぜですか?)。AWSCore ライブラリの内容を確認したところ、次のことdefaultExecutorがわかりました。

+ (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彼らの声明によると、 my はキューcontinuationBlockで実行されることが保証されていませんでした。DISPATCH_QUEUE_PRIORITY_DEFAULTそこで、共有dispatch_queue_tキューを 1 つ作成し、そのすべての操作をperformBlockAndWait:CoreData メソッドと組み合わせて呼び出します。その結果、現在クラッシュは発生しておらず、新しいリリースを提出しました。contextゾンビに関するクラッシュ レポートが得られない場合は、この投稿を更新します。

于 2016-12-12T06:02:22.127 に答える
1

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

不必要に多くのスレッドを作成しているように見えます。AWSContinuationBlockandを採用するのAWSExecutorは得策ではないと思います。synchronizeMyWordsバックグラウンド スレッドから呼び出すことができます。ブロックはバックグラウンド スレッドで実行される可能性があります。ブロック内で、子コンテキストにリンクされた新しいバックグラウンド スレッドを作成します。loadLocalWords何が返されるか、またはcontinueWithExecutor:block:スレッドをどのように処理するかは明確ではありません。

データの保存にも問題があります。子コンテキストが保存された後、メイン コンテキストは保存されません。おそらくこれは後で発生しますが、おそらく他の操作に関連して発生するため、コードが以前に機能していたという事実は、おそらく「誤検知」です。

私の推奨事項は、スレッド コードを単純化することです。Core Data ブロック API に限定する必要があります。

于 2016-12-10T09:31:09.460 に答える