1

我有一些代码需要一些时间来处理,因此它不应该在主队列上运行。但是我不确定如何正确“构造” GCD 代码段。即每次应用程序激活时,我都在进行同步操作:

AppDelegate.m

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

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

    [abHelper sync];
}

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

地址簿助手.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;
    });

核心数据线程安全

您没有向我们展示您如何访问 Core Data。您需要注意 Core Data 对象通常不是线程安全的。您的后台同步操作应创建自己的托管对象上下文,作为主托管对象上下文的子项。然后,当操作完成时,它可以告诉子上下文保存,更改将以线程安全的方式推送到父上下文。

由于您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 回答