From 57da89896c61eb8c6d3d2adb9a44f7e211fbfdc1 Mon Sep 17 00:00:00 2001 From: Patrick Hernandez Date: Sun, 8 Apr 2012 22:31:35 -0500 Subject: [PATCH] Update to support batch completion block firing after all dependent completion blocks. - Added dispatch group property to AFHTTPRequestOperation - Modified all request objects to call dispatch_group_async in completion blocks - Added a dispatch semaphore for thread safety of the dispatch group property - Added dispatch_group_enter and dispatch_group_leave calls to ensure requests are always in the group until the completion block finishes. - Added an override for setCompletionBlock to call dispatch_group_leave so that subclasses do not need the call in each completion block. - Modified enqueueBatchOfHTTPRequestOperations to now dispatch_group_notify in the NSBlockOperation completion block --- AFNetworking/AFHTTPClient.m | 9 ++- AFNetworking/AFHTTPRequestOperation.h | 4 ++ AFNetworking/AFHTTPRequestOperation.m | 60 ++++++++++++++++++- AFNetworking/AFImageRequestOperation.m | 6 +- AFNetworking/AFJSONRequestOperation.m | 8 +-- AFNetworking/AFPropertyListRequestOperation.m | 8 +-- AFNetworking/AFXMLRequestOperation.m | 6 +- 7 files changed, 83 insertions(+), 18 deletions(-) diff --git a/AFNetworking/AFHTTPClient.m b/AFNetworking/AFHTTPClient.m index 60b44a4..a5e2b2c 100644 --- a/AFNetworking/AFHTTPClient.m +++ b/AFNetworking/AFHTTPClient.m @@ -515,9 +515,11 @@ static void AFReachabilityCallback(SCNetworkReachabilityRef __unused target, SCN progressBlock:(void (^)(NSUInteger numberOfCompletedOperations, NSUInteger totalNumberOfOperations))progressBlock completionBlock:(void (^)(NSArray *operations))completionBlock { + dispatch_group_t dispatchGroup = dispatch_group_create(); + NSBlockOperation *batchedOperation = [NSBlockOperation blockOperationWithBlock:^{ if (completionBlock) { - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{ completionBlock(operations); }); } @@ -529,9 +531,10 @@ static void AFReachabilityCallback(SCNetworkReachabilityRef __unused target, SCN for (AFHTTPRequestOperation *operation in operations) { AFCompletionBlock originalCompletionBlock = [[operation.completionBlock copy] autorelease]; + operation.dispatchGroup = dispatchGroup; operation.completionBlock = ^{ if (progressBlock) { - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_group_async(dispatchGroup, dispatch_get_main_queue(), ^{ progressBlock([[batchedOperation.dependencies filteredArrayUsingPredicate:finishedOperationPredicate] count], [batchedOperation.dependencies count]); }); } @@ -544,6 +547,8 @@ static void AFReachabilityCallback(SCNetworkReachabilityRef __unused target, SCN [batchedOperation addDependency:operation]; [self enqueueHTTPRequestOperation:operation]; } + + dispatch_release(dispatchGroup); } #pragma mark - diff --git a/AFNetworking/AFHTTPRequestOperation.h b/AFNetworking/AFHTTPRequestOperation.h index 122099e..c43c8ca 100644 --- a/AFNetworking/AFHTTPRequestOperation.h +++ b/AFNetworking/AFHTTPRequestOperation.h @@ -83,6 +83,10 @@ */ @property (nonatomic) dispatch_queue_t failureCallbackQueue; +/** + The dispatch group on which to call the completion/failure block + */ +@property (nonatomic) dispatch_group_t dispatchGroup; /** A Boolean value determining whether or not the class can process the specified request. For example, `AFJSONRequestOperation` may check to make sure the content type was `application/json` or the URL path extension was `.json`. diff --git a/AFNetworking/AFHTTPRequestOperation.m b/AFNetworking/AFHTTPRequestOperation.m index 871cb18..cb96e36 100644 --- a/AFNetworking/AFHTTPRequestOperation.m +++ b/AFNetworking/AFHTTPRequestOperation.m @@ -56,6 +56,8 @@ static NSString * AFStringFromIndexSet(NSIndexSet *indexSet) { @interface AFHTTPRequestOperation () @property (readwrite, nonatomic, retain) NSError *HTTPError; +@property (nonatomic) dispatch_once_t onceToken; +@property (atomic) dispatch_semaphore_t dispatchSemaphore; @end @implementation AFHTTPRequestOperation @@ -64,6 +66,9 @@ static NSString * AFStringFromIndexSet(NSIndexSet *indexSet) { @synthesize HTTPError = _HTTPError; @synthesize successCallbackQueue = _successCallbackQueue; @synthesize failureCallbackQueue = _failureCallbackQueue; +@synthesize dispatchGroup = _dispatchGroup; +@synthesize onceToken = _onceToken; +@synthesize dispatchSemaphore = _dispatchSemaphore; - (id)initWithRequest:(NSURLRequest *)request { @@ -73,6 +78,7 @@ static NSString * AFStringFromIndexSet(NSIndexSet *indexSet) { } self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)]; + self.dispatchSemaphore = dispatch_semaphore_create(1); return self; } @@ -92,6 +98,16 @@ static NSString * AFStringFromIndexSet(NSIndexSet *indexSet) { _failureCallbackQueue = NULL; } + if (_dispatchGroup) { + dispatch_release(_dispatchGroup); + _dispatchGroup = NULL; + } + + if (_dispatchSemaphore) { + dispatch_release(_dispatchSemaphore); + _dispatchSemaphore = NULL; + } + [super dealloc]; } @@ -157,6 +173,46 @@ static NSString * AFStringFromIndexSet(NSIndexSet *indexSet) { } } +- (void)setDispatchGroup:(dispatch_group_t)dispatchGroup { + dispatch_semaphore_wait(self.dispatchSemaphore, DISPATCH_TIME_FOREVER); + if (dispatchGroup != _dispatchGroup) { + if (_dispatchGroup) { + dispatch_group_leave(_dispatchGroup); + dispatch_release(_dispatchGroup); + _dispatchGroup = NULL; + } + + if (dispatchGroup) { + dispatch_retain(dispatchGroup); + _dispatchGroup = dispatchGroup; + dispatch_group_enter(_dispatchGroup); + } + } + dispatch_semaphore_signal(self.dispatchSemaphore); +} + +- (dispatch_group_t)dispatchGroup { + dispatch_semaphore_wait(self.dispatchSemaphore, DISPATCH_TIME_FOREVER); + if(_dispatchGroup == NULL) { + _dispatchGroup = dispatch_group_create(); + dispatch_group_enter(_dispatchGroup); + } + dispatch_semaphore_signal(self.dispatchSemaphore); + return _dispatchGroup; +} + +- (void)setCompletionBlock:(void (^)(void))block { + [super setCompletionBlock:^{ + if(block) { + block(); + } + // Dispatch once is used to ensure that setting the block with this block will not cause multiple calls to 'dispatch_group_leave' + dispatch_once(&_onceToken, ^{ + dispatch_group_leave(self.dispatchGroup); + }); + }]; +} + - (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure { @@ -167,13 +223,13 @@ static NSString * AFStringFromIndexSet(NSIndexSet *indexSet) { if (self.error) { if (failure) { - dispatch_async(self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{ + dispatch_group_async(self.dispatchGroup, self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{ failure(self, self.error); }); } } else { if (success) { - dispatch_async(self.successCallbackQueue ? self.successCallbackQueue : dispatch_get_main_queue(), ^{ + dispatch_group_async(self.dispatchGroup, self.successCallbackQueue ? self.successCallbackQueue : dispatch_get_main_queue(), ^{ success(self, self.responseData); }); } diff --git a/AFNetworking/AFImageRequestOperation.m b/AFNetworking/AFImageRequestOperation.m index 971cd14..6f629e4 100644 --- a/AFNetworking/AFImageRequestOperation.m +++ b/AFNetworking/AFImageRequestOperation.m @@ -228,10 +228,10 @@ static dispatch_queue_t image_request_operation_processing_queue() { return; } - dispatch_async(image_request_operation_processing_queue(), ^(void) { + dispatch_group_async(self.dispatchGroup, image_request_operation_processing_queue(), ^(void) { if (self.error) { if (failure) { - dispatch_async(self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{ + dispatch_group_async(self.dispatchGroup, self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{ failure(self, self.error); }); } @@ -245,7 +245,7 @@ static dispatch_queue_t image_request_operation_processing_queue() { image = self.responseImage; - dispatch_async(self.successCallbackQueue ? self.successCallbackQueue : dispatch_get_main_queue(), ^{ + dispatch_group_async(self.dispatchGroup, self.successCallbackQueue ? self.successCallbackQueue : dispatch_get_main_queue(), ^{ success(self, image); }); } diff --git a/AFNetworking/AFJSONRequestOperation.m b/AFNetworking/AFJSONRequestOperation.m index 9046dd0..dacacd9 100644 --- a/AFNetworking/AFJSONRequestOperation.m +++ b/AFNetworking/AFJSONRequestOperation.m @@ -125,23 +125,23 @@ static dispatch_queue_t json_request_operation_processing_queue() { if (self.error) { if (failure) { - dispatch_async(self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{ + dispatch_group_async(self.dispatchGroup, self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{ failure(self, self.error); }); } } else { - dispatch_async(json_request_operation_processing_queue(), ^(void) { + dispatch_group_async(self.dispatchGroup, json_request_operation_processing_queue(), ^(void) { id JSON = self.responseJSON; if (self.JSONError) { if (failure) { - dispatch_async(self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{ + dispatch_group_async(self.dispatchGroup, self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{ failure(self, self.error); }); } } else { if (success) { - dispatch_async(self.successCallbackQueue ? self.successCallbackQueue : dispatch_get_main_queue(), ^{ + dispatch_group_async(self.dispatchGroup, self.successCallbackQueue ? self.successCallbackQueue : dispatch_get_main_queue(), ^{ success(self, JSON); }); } diff --git a/AFNetworking/AFPropertyListRequestOperation.m b/AFNetworking/AFPropertyListRequestOperation.m index ea9d216..e529a74 100644 --- a/AFNetworking/AFPropertyListRequestOperation.m +++ b/AFNetworking/AFPropertyListRequestOperation.m @@ -125,23 +125,23 @@ static dispatch_queue_t property_list_request_operation_processing_queue() { if (self.error) { if (failure) { - dispatch_async(self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{ + dispatch_group_async(self.dispatchGroup, self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{ failure(self, self.error); }); } } else { - dispatch_async(property_list_request_operation_processing_queue(), ^(void) { + dispatch_group_async(self.dispatchGroup, property_list_request_operation_processing_queue(), ^(void) { id propertyList = self.responsePropertyList; if (self.propertyListError) { if (failure) { - dispatch_async(self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{ + dispatch_group_async(self.dispatchGroup, self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{ failure(self, self.error); }); } } else { if (success) { - dispatch_async(self.successCallbackQueue ? self.successCallbackQueue : dispatch_get_main_queue(), ^{ + dispatch_group_async(self.dispatchGroup, self.successCallbackQueue ? self.successCallbackQueue : dispatch_get_main_queue(), ^{ success(self, propertyList); }); } diff --git a/AFNetworking/AFXMLRequestOperation.m b/AFNetworking/AFXMLRequestOperation.m index 79c5272..d4f4268 100644 --- a/AFNetworking/AFXMLRequestOperation.m +++ b/AFNetworking/AFXMLRequestOperation.m @@ -170,18 +170,18 @@ static dispatch_queue_t xml_request_operation_processing_queue() { return; } - dispatch_async(xml_request_operation_processing_queue(), ^(void) { + dispatch_group_async(self.dispatchGroup, xml_request_operation_processing_queue(), ^(void) { NSXMLParser *XMLParser = self.responseXMLParser; if (self.error) { if (failure) { - dispatch_async(self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{ + dispatch_group_async(self.dispatchGroup, self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{ failure(self, self.error); }); } } else { if (success) { - dispatch_async(self.successCallbackQueue ? self.successCallbackQueue : dispatch_get_main_queue(), ^{ + dispatch_group_async(self.dispatchGroup, self.successCallbackQueue ? self.successCallbackQueue : dispatch_get_main_queue(), ^{ success(self, XMLParser); }); }