From 3c9f09b9595699d148c8e8461d57b9cd542086c5 Mon Sep 17 00:00:00 2001 From: Patrick Hernandez Date: Sun, 8 Apr 2012 18:18:13 -0500 Subject: [PATCH 1/4] commiting the .gitignore file --- .gitignore | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f9f28d5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +*.swp +*~.nib + +build/ + +*.pbxuser +*.perspective +*.perspectivev3 + +*.mode1v3 +*.mode2v3 + +xcuserdata \ No newline at end of file From 57da89896c61eb8c6d3d2adb9a44f7e211fbfdc1 Mon Sep 17 00:00:00 2001 From: Patrick Hernandez Date: Sun, 8 Apr 2012 22:31:35 -0500 Subject: [PATCH 2/4] 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); }); } From e47fc50903ba4700fafa2ff1d83eb14e242daf91 Mon Sep 17 00:00:00 2001 From: Patrick Hernandez Date: Sun, 8 Apr 2012 23:04:09 -0500 Subject: [PATCH 3/4] removing the .gitignore --- .gitignore | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index f9f28d5..0000000 --- a/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -.DS_Store -*.swp -*~.nib - -build/ - -*.pbxuser -*.perspective -*.perspectivev3 - -*.mode1v3 -*.mode2v3 - -xcuserdata \ No newline at end of file From 8b94ef8e442e56d5bd59e65421090d61008e001e Mon Sep 17 00:00:00 2001 From: Patrick Hernandez Date: Mon, 9 Apr 2012 00:19:28 -0500 Subject: [PATCH 4/4] Optimized how enqueueing operations works - Removed the NSBlockOperation as it was not longer needed for completion block to be fired appropriately - Added a safety call to setCompletionBlock in AFHTTPRequestOperation to ensure that dispatch_group_leave would be called --- AFNetworking/AFHTTPClient.m | 20 +++++++------------- AFNetworking/AFHTTPRequestOperation.m | 1 + 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/AFNetworking/AFHTTPClient.m b/AFNetworking/AFHTTPClient.m index a5e2b2c..3d51efb 100644 --- a/AFNetworking/AFHTTPClient.m +++ b/AFNetworking/AFHTTPClient.m @@ -517,16 +517,6 @@ static void AFReachabilityCallback(SCNetworkReachabilityRef __unused target, SCN { dispatch_group_t dispatchGroup = dispatch_group_create(); - NSBlockOperation *batchedOperation = [NSBlockOperation blockOperationWithBlock:^{ - if (completionBlock) { - dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{ - completionBlock(operations); - }); - } - }]; - - [self.operationQueue addOperation:batchedOperation]; - NSPredicate *finishedOperationPredicate = [NSPredicate predicateWithFormat:@"isFinished == YES"]; for (AFHTTPRequestOperation *operation in operations) { @@ -535,7 +525,7 @@ static void AFReachabilityCallback(SCNetworkReachabilityRef __unused target, SCN operation.completionBlock = ^{ if (progressBlock) { dispatch_group_async(dispatchGroup, dispatch_get_main_queue(), ^{ - progressBlock([[batchedOperation.dependencies filteredArrayUsingPredicate:finishedOperationPredicate] count], [batchedOperation.dependencies count]); + progressBlock([[operations filteredArrayUsingPredicate:finishedOperationPredicate] count], [operations count]); }); } @@ -543,11 +533,15 @@ static void AFReachabilityCallback(SCNetworkReachabilityRef __unused target, SCN originalCompletionBlock(); } }; - - [batchedOperation addDependency:operation]; [self enqueueHTTPRequestOperation:operation]; } + if (completionBlock) { + dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{ + completionBlock(operations); + }); + } + dispatch_release(dispatchGroup); } diff --git a/AFNetworking/AFHTTPRequestOperation.m b/AFNetworking/AFHTTPRequestOperation.m index cb96e36..c9f2dad 100644 --- a/AFNetworking/AFHTTPRequestOperation.m +++ b/AFNetworking/AFHTTPRequestOperation.m @@ -79,6 +79,7 @@ static NSString * AFStringFromIndexSet(NSIndexSet *indexSet) { self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)]; self.dispatchSemaphore = dispatch_semaphore_create(1); + self.completionBlock = NULL; return self; }