1

処理に少し時間がかかるコードがあるため、メインキューで実行しないでください。ただし、GCDコードセグメントを正しく「構造化」する方法がわかりません。つまり、アプリがアクティブになるたびに、同期操作を実行しています。

AppDelegate.m

- (void)applicationDidBecomeActive:(UIApplication *)application {

    AddressBookHelper *abHelper = [AddressBookHelper sharedInstance]; // singleton helper class of NSObject

    [abHelper sync];
}

AddressBookHelper内の同期コードは次のようになります。

AddressBookHelper.m

- (void)sync {

    NSArray *people = // Fetching some people from Core Data

    NSMutableArray *syncConflicts;
    // Start doing some business logic, iterating over data and so on

    for (id object in people) {
    // Process some data
        [syncConflicts addObject:object];
    }

    self.syncConflicts = syncConflicts;

    // I have separated this method to keep the code cleaner and to separate the logic of the methods
    [self processSyncConflicts];
}

- (void)processSyncConflicts {

    if ([self.syncConflicts count] > 0) {
        // Alert the user about the sync conflict by showing a UIAlertView to take action
        UIAlertView *alert;
        [alert show];

    } else {

        // Syncing is complete
    }
}

したがって、このコード構造では、GCDを適切に使用してこのコードをバックグラウンドスレッドに配置するにはどうすればよいでしょうか。

これを行うのと同じくらい簡単ですか?

AppDelegate.m

- (void)applicationDidBecomeActive:(UIApplication *)application {

    AddressBookHelper *abHelper = [AddressBookHelper sharedInstance]; // singleton helper class of NSObject

    dispatch_queue_t queue = dispatch_queue_create("addressbookSyncQueue", 0);
    dispatch_async(queue, ^{
            [abHelper sync];
    });
}

AddressBookHelper.m

- (void)processSyncConflicts {

    if ([self.syncConflicts count] > 0) {
        // Alert the user about the sync conflict by showing a UIAlertView to take action
        UIAlertView *alert;
        dispatch_queue_t mainQueue = dispatch_get_main_queue();

        dispatch_async(mainQueue, ^{
            [alert show];
        });

    } else {

        // Syncing is complete
    }
}
4

1 に答える 1

7

コードにいくつかの潜在的な問題があります。

複数の同時同期

を受け取っapplicationDidBecomeActive:たが、終了する前[abHelper sync]に、ユーザーがアプリから離れてからアプリに戻ったとします。これで、別のキューを受け取りapplicationDidBecomeActive:、新しいGCDキューを作成し[abHelper sync]、最初のキューがまだ実行されている間に別のキューを開始します。悪い!

クラスを変更AddressBookHelperして、独自のGCDキューを作成し、インスタンス変数に格納します。同期操作がそれ自体をそのキューに置くようにインターフェースを変更します。例:

@interface AddressBookHelper : NSObject

+ (AddressBookHelper *)sharedInstance;

- (void)syncInBackground;

@end

@implementation AddressBookHelper {
    dispatch_queue_t queue_;
}

- (void)syncInBackground {
    if (!queue_) {
        queue_ = dispatch_queue_create("AddressBookHelper", 0);
    }
    dispatch_async(queue_, ^{ [self syncOnCurrentQueue]; });
}

- (void)syncOnCurrentQueue {
    // This is now a private method that always runs on queue_.
    // ...
}

@end

プロパティスレッドセーフ

syncConflictsあなたはそれをプロパティに保存しているので、あなたのメインスレッドはにアクセスする必要があると思います。その場合syncConflictsは、メインキューを更新して、それを使用しているメインキュー操作の途中で変更しないようにする必要があります。

    dispatch_sync(dispatch_get_main_queue(), ^{
        self.syncConflicts = syncConflicts;
    });

コアデータスレッドセーフ

CoreDataへのアクセス方法を教えてくれませんでした。CoreDataオブジェクトは一般的にスレッドセーフではないことに注意する必要があります。バックグラウンド同期操作では、メインの管理対象オブジェクトコンテキストの子として、独自の管理対象オブジェクトコンテキストを作成する必要があります。次に、操作が完了すると、子コンテキストに保存するように指示でき、変更はスレッドセーフな方法で親コンテキストにプッシュされます。

この子コンテキストからオブジェクトを取得するため、peopleオブジェクトをメインスレッドに戻すことはできません。代わりに、各オブジェクトを保存しobjectID、それを使用してメインスレッドのコンテキストを使用してオブジェクトを再作成する必要があります。

- (void)syncOnCurrentQueue {
    // This is now a private method that always runs on queue_.

    NSManagedObjectContext *syncContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
    syncContext.parentContext = self.mainContext;

    NSArray *people = nil; // Fetching some people from syncContext

    NSMutableArray *syncConflictIDs = [[NSMutableArray alloc] init];
    // Start doing some business logic, iterating over data and so on

    for (id object in people) {
        // Process some data
        [syncConflictIDs addObject:[object objectID]];
    }

    NSError *error;
    if (![syncContext save:&error]) {
        // save to main context failed...
    }

    dispatch_sync(dispatch_get_main_queue(), ^{
        // assuming main context has NSMainQueueConcurrencyType
        NSMutableArray *syncConflicts = [[NSMutableArray alloc] initWithCapacity:syncConflictIDs.count];
        for (NSManagedObjectID *objectID in syncConflictIDs) {
            [syncConflicts addObject:[self.mainContext objectWithID:objectID]];
        }
        self.syncConflicts = syncConflicts;
    });

    // I have separated this method to keep the code cleaner and to separate the logic of the methods
    [self processSyncConflicts];
}

UIKitスレッド-安全性

UIAlertViewメインスレッドのようにUIKitオブジェクトのみを操作できます。アラートビューの場所を実際に表示していませalloc/initんが、変数を宣言した場所で指定されたメインスレッドにアラートビューを強制的に配置していないと思います。メインスレッドでそれを行うことを確認する必要があります。

- (void)processSyncConflicts {
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([self.syncConflicts count] > 0) {
            // Alert the user about the sync conflict by showing a UIAlertView to take action
            UIAlertView *alert = [[UIAlertView alloc] init...];
            [alert show];
        } else {
            // Syncing is complete
        }
    }
}
于 2012-12-04T22:25:36.850 に答える