I have some code that takes a little time to process and thus appropriately it should not run on the main queue. However I’m not sure on how to correctly “structure” the GCD code segments. I.e every time the app becomes active I’m doing a syncing operation:
AppDelegate.m
- (void)applicationDidBecomeActive:(UIApplication *)application {
AddressBookHelper *abHelper = [AddressBookHelper sharedInstance]; // singleton helper class of NSObject
[abHelper sync];
}
The syncing code inside AddressBookHelper looks something like this:
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
}
}
So with this code structure, how would I properly use GCD to put this code on a background thread?
Is it as easy as doing this?
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
}
}
I see several potential problems in your code.
Multiple Simultaneous Syncs
Suppose you receive
applicationDidBecomeActive:, but before[abHelper sync]finishes, the user switches away from your app, and then back to your app. Now you receive anotherapplicationDidBecomeActive:, create a new GCD queue, and start another[abHelper sync]while the first one is still running. Bad!Change your
AddressBookHelperclass to create its own GCD queue and store it in an instance variable. Change the interface so that the sync operation puts itself on that queue. Example:Property Thread-Safety
I assume your main thread needs to access
syncConflicts, since you’re storing it in a property. If so, you should updatesyncConflictson the main queue, so that you don’t change it in the middle of some main-queue operation that is using it.Core Data Thread-Safety
You didn’t show us how you access Core Data. You need to be aware that Core Data objects are not generally thread-safe. Your background sync operation should create a managed object context of its own, as a child of the main managed object context. Then, when the operation is complete, it can tell the child context to save, and the changes will be pushed to the parent context in a thread-safe way.
Since you’ll be getting your
peopleobjects from this child context, you can’t pass them back to the main thread. Instead, you need to store each object’sobjectID, and use that to recreate the objects using the main thread’s context.UIKit Thread-Safety
You can only manipulate UIKit objects like
UIAlertViewon the main thread. You didn’t actually show where youalloc/inityour alert view, but I assume you’re not forcing it to be on the main thread given where you declared the variable. You need to make sure you do it on the main thread.