timpone
timpone

Reputation: 19969

Is it possible in Objective-C to call a method after a set of other methods are finished executing

This might be a bit of a naive question but in my loading ViewController, I am loading all the content that the app needs using a a set of methods like getEachItem below. This is usually like 2 or 3 items and they are all written to a cache.

I'd like to call the method 'showNavigation' that runs after the final instance of getEachItem has completed but not sure how to do this. getEachItem does a GET request with AFNetworking. Something like a jQuery complete block but for the totality of the below for loop.

NSArray *tmpItems=[result objectForKey:@"ipad_items"];
for(NSDictionary *m in tmpItems){
  // will also increment into the _menus array
  [self getEachItem:[m objectForKey:@"id"]];
  [self getImages:[m objectForKey:@"id"]];
}
[self showNavigation];

Upvotes: 2

Views: 645

Answers (1)

Rob
Rob

Reputation: 437582

When you call the AFNetworking GET method, it returns a AFHTTPRequestOperation (a NSOperation subclass). You can leverage this fact to employ an operation queue-based solution to your problem. Namely, you can create a new "completion operation" which is dependent upon the completion of particular AFNetworking operations.

For example, you might change your getEachItem method to return the the AFHTTPRequestOperation returned by the GET method. For example, let's assume you have a getEachItem is currently defined something like:

- (void)getEachItem:(id)identifier
{
     // do a lot of stuff

     [self.manager GET:... parameters:... success:... failure:...];
}

Change that to:

- (NSOperation *)getEachItem:(id)identifier
{
    // do a lot of stuff

    return [self.manager GET:... parameters:... success:... failure:...];
}

Then, you can create your own completion operation which will be dependent upon all those other AFHTTPRequestOperation operations finishing. Thus:

NSOperation *completion = [NSBlockOperation blockOperationWithBlock:^{
    [self showNavigation];
}];

NSArray *tmpItems=[result objectForKey:@"ipad_items"];
for(NSDictionary *m in tmpItems){
    // will also increment into the _menus array
    NSOperation *operation = [self getEachItem:[m objectForKey:@"id"]];
    [completion addDependency:operation];
    [self getImages:[m objectForKey:@"id"]];
}

[[NSOperationQueue mainQueue] addOperation:completion];

Once you've done that, the completion operation will not fire until all of the getEachItem operations complete. Note, this completion operation will fire when the core AFHTTPRequestOperation objects finish, but there are no assurances that their respective completion blocks of those requests are necessarily done.


Another approach is to use a GCD "group". Using this technique, you "enter" the group when you submit each request, you "leave" the group in your completion blocks of the GET method. You can then specify a block of code to be performed when the the group notifies you that you have left the group as many times as you've entered it (i.e. all of the AFNetworking network requests, and their success/failure blocks, are finished).

For example, add a dispatch_group_t parameter to getEachItem:

- (void)getEachItem:(id)identifier group:(dispatch_group_t)group
{
    dispatch_group_enter(group);

    // do a lot of stuff

    [self.manager GET:... parameters:... success:^(...) {
        // do you success stuff and when done, leave the group

        dispatch_group_leave(group);
    } failure:^(...) {
        // do you failure stuff and when done, leave the group

        dispatch_group_leave(group);
    }];
}

Note, you "enter" the group before submitting the request, and both the success and failure blocks must call dispatch_group_leave.

Having done that, you can now use the dispatch_group_t in your loop of requests, performing the showNavigation when the group receives its notification that everything is done:

dispatch_group_t group = dispatch_group_create();

NSArray *tmpItems=[result objectForKey:@"ipad_items"];
for(NSDictionary *m in tmpItems){
    // will also increment into the _menus array
    [self getEachItem:[m objectForKey:@"id"] group:group];
    [self getImages:[m objectForKey:@"id"]];
}

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    [self showNavigation];
});

Upvotes: 4

Related Questions