Reputation: 69
I have two classes, a view controller and another (TwitterCrawler) performing network requests to Twitter API.I make the first network request when the view controller loads and the second one when the user pushes a button.
I want that the second request is performed when the first is completed, it means that if the user pushes the button before the first request is completed, it waits for it to end and then execute the second.
Even if I put the first request on a queue I create, the command dispatch_sync
I call when the user pushes the button is called before the queue is emptied.
Here it is my code:
//TwitterCrawler.m
-(void) getTwitterUsersInPosition: (Position*) position
{
dispatch_queue_async(twitterQueue, ^ {
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[accountStore requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error){
if (granted) {
NSArray *accounts = [accountStore accountsWithAccountType:accountType];
if (accounts.count > 0)
{
ACAccount *twitterAccount = [accounts objectAtIndex:0];
NSArray *keys = [NSArray arrayWithObjects:@"geocode", @"count", nil];
NSArray *objects = [NSArray arrayWithObjects:@"37.781157,-122.398720,100mi", @"100", nil];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:objects
forKeys:keys];
SLRequest *twitterInfoRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodGET URL:[NSURL URLWithString:@"https://api.twitter.com/1.1/search/tweets.json"] parameters:dictionary];
[twitterInfoRequest setAccount:twitterAccount];
// Making the request
[twitterInfoRequest performRequestWithHandler: ^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
//dispatch_async(dispatch_get_main_queue(), ^{
dispatch_async(twitterQueue, ^{
...adding object to nsmutablearray* twitterUsers
});
}];
}
}
else {
NSLog(@"No access granted");
}
}];
});
}
-(void) printUsers
{
// wait for queue to empty
dispatch_sync (twitterQueue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
for (NSString* str in twitterUsers)
{
NSLog(@"%@",str);
}
});
});
}
//ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
twitter_crawler=[[TwitterCrawler alloc] initTwitterCrawler];
[twitter_crawler getTwitterUsersInPosition:position];
}
- (IBAction)pressButton:(id)sender {
[twitter_crawler printUsers];
}
So if the user pushes the button before twitterUsers
is filled with data coming from the first request, it returns an empty array.
EDIT:
the queue is serial, I created it in this way:
dispatch_queue_t twitterQueue = dispatch_queue_create("com.example.MyQueue", NULL);
Upvotes: 0
Views: 422
Reputation: 162722
dispatch_sync
guarantees that it will not return until the block enqueued via that API is executed. No more, no less.
If twitterQueue
is a concurrent queue, then the block enqueued via dispatch_sync
will be executed potentially immediately, regardless of what other blocks are doing. If it is a serial queue, then the block will be executed after what was already enqueued. However, if new items are added to the queue, they'll operate after the synchronous dispatch.
The notion of doing something when a queue is emptied isn't generally a useful approach (unless you are hard managing the queue and the last item executed is guaranteed to be the last item of that task, potentially triggering a new multi-block task and re-using the queue).
You are better of using semaphores or dispatch barriers or any of a number of other grouping mechanisms to control your concurrency tightly, including when updates to the main queue happen.
This is a good starting point document. There are some more advanced concurrency guides available, too.
Upvotes: 4