In my current project I have a number of data services classes, each one dedicated to a specific source of data so that it's responsible of downloading data from its API, checking for local database and doing inserts or updates as necessary. I needed to synchronize access to the entry point method for each of these classes as they may be consumed by other classes (like view models in the project architecture). My first thought was to use @synchronized, but it uses locks under the hood and Apple's advice was to avoid that and use serial queues instead.I ended up creating a static shared serial queue for every data service class to be used by its instance entry point method as those classes are not singletons.I also used dispatch_suspend and dispatch_resume to control the flow of blocks being executed by this serial queue.It's working, and now for a specific data service class (DS), if we have 2 instances of it DS1 and DS2 then called DS1.loadData() and DS2.loadData successively, DS2.loadData would be executed after DS1.loadData() has finished. This also has the benefit of other instances of the other data services classes working concurrently.Could this approach impose any risks or issues?Data service class:// UserDataService.m@interface UserDataService () @property (strong, nonatomic) dispatch_queue_t serialQueue;@end@implementation UserDataService- (instancetype)init {    self = [super init];    if (self) {        _serialQueue = [self sharedQueue];    }    return self;}// Private Method- (dispatch_queue_t)sharedQueue{    static dispatch_queue_t sharedQueue;    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        sharedQueue = dispatch_queue_create(com.UserDataService.queue, DISPATCH_QUEUE_SERIAL);    });    return sharedQueue;}// Public Method// This is the entry point method for this data service class- (void)loadDataWithSuccess:(DataSuccessBlock)success failure:(DataFailureBlock)failure {    // Check for data in database (Realm database), if not download from server    dispatch_async(self.serialQueue, ^{        NSArray *results = [User allObjects];   // User is RLMObject (Realm object)        if (results && results.count > 0) {            dispatch_async(dispatch_get_main_queue(), ^{                success([User allObjects]);                 });        } else {            dispatch_suspend(self.serialQueue);            // Start to download data and persist it into local database            [self downloadAndPersistDataWithSuccess:^(RLMResults *result) {                dispatch_async(dispatch_get_main_queue(), ^{                    dispatch_resume(self.serialQueue);                    success(result);                });            } failure:^(NSString *errorMsg) {                dispatch_resume(self.serialQueue);                failure(errorMsg);            }];        }    });}- (void)downloadAndPersistDataWithSuccess:(DataSuccessBlock)success failure:(DataFailureBlock)failure {    // Download data from server and persist it in local database    APIService *apiService = [APIService new];  //APIService class wraps AFNetworking GET methods    [apiService getUsersWithsuccess:^(NSDictionary *result) {        //persist data        [self persistData:result withSuccess:^(RLMResults *persistedObjects) {            success(persistedObjects);        } failure:^(NSString *errorMsg) {            failure(errorMsg);        }];    } failure:^(NSString *errorMsg) {        failure(errorMsg);    }];}- (void)persistData:(NSDictionary *)data withSuccess:(DataSuccessBlock)success failure:(DataFailureBlock)failure {    if (data) {        NSArray *users = [data valueForKey:@users];        RLMRealm *realm;        @try {            realm = [RLMRealm defaultRealm];            [realm beginWriteTransaction];            for (NSDictionary *userData in users) {                // Create user realm object                User *user = [[User alloc] initWithDictionary:userData];                [realm addOrUpdateObject:user];            }            [realm commitWriteTransaction];            success([User allObjects]);        }        @catch (NSException *exception) {            if ([realm inWriteTransaction]) {                [realm cancelWriteTransaction];            }            failure([NSString stringWithFormat:@%@ - %@, exception.name, exception.reason]);        }    }}@end
Synchronize access to an instance method using a static serial queue
objective c;synchronization;grand central dispatch