AFNetworking/AFNetworking — pull request #288 by kirsplatrick (2012-04-09 18:34:17+00:00)
Title: Update to support batch completion block firing after all dependent completion blocks.

This update is to create a solution for batch request completion block methods firing before all operation completion blocks have finished. This has come up in a couple other issues (#206 and #198). I have also had this problem bite me in the past. I feel @kcharwood and I have come up with a pretty good solution using GCD group dispatching.

The overall idea is that every `AFHTTPRequestOperation` has a public `dispatch_group_t` property. When this property is set it calls `dispatch_group_enter` to become part of the group. It will only leave once the completion block has fired using `dispatch_group_async`. When enqueuing a batch request the method now creates a `dispatch_group_t` object and sets the property of each operation. The method then calls `dispatch_group_notify` so that it will fire once all completion blocks have fired.

The other option that was mentioned was to run through all the operations in the completion block of the queue and handle completions there. I feel like that is an inconvenient fix that invalidates the use of individual completion blocks for operations and forces code to be placed in the completion for the entire batch. This also brings another problem of identifying which operation is which. We are only given the collection and so an identifier property would need to be added to identify which operation is which when handling the return responses.

Overall, I think that what we have submitted is the proper way to fix this issue even though it is a fairly large change.

A few implementation notes:
* For thread safety, each `AFHTTPRequestOperation` has a new private `dispatchSemaphore`. This is used when setting and getting the `_dispatchGroup`.
* `AFHTTPRequestOperation` now overrides `-(void)setCompletionBlock:`, and leaves the `dispatchGroup` there. That ensures that the operation properly leaves the group if the success or failure blocks are nil, or if the operation has been canceled. This is also encapsulated in a `dispatchOnce` to ensure the operation cannot over leave the group if someone copies the completion block, and sets a new one (for example, the progress call back on the batch request). If we did not set this here, we would need to call it at the end of every completion block of all subclasses, which seemed like a bad idea going forward.
* Note that when we set the dispatch group, the operation enters it. This enter is counter balanced with the leave above. We must enter it in case there is a delay from when the completion block is called and when the `dispatch_group_async` fires within the completion block.
* A `self.completionBlock = NULL` has been added to the `initWithRequest` method. This is so if the completion block is never set anywhere else, we will have the balanced `dispatch_group_leave` call in the completion block. This case occurs if a user decides to explicitly create a `AFHTTPRequestOperation` without using the `AFHTTPClient` methods and does not set the completion block.
* Backwards Compatibility: This would be a breaking change going forward. All subclasses of `AFHTTPRequestOperation` that override `setCompletionBlockWithSuccess:failure` MUST change from `dispatch_async` to `dispatch_group_async`. I have made the change to all `AFHTTPRequestOperation` subclasses here in this pull request, but any dev who has created subclasses needs to be aware of this change in sometime of upgrade note document.
* The `AFHTTPClient` now uses `dispatch_group_notify` instead of a NSBlockOperation to fire a completion block once all dependent operations have completed. This guarantees that the completion block of the batch will fire after the success/failure block of each dependent operation is done.