Reputation: 624
I have to folowing method.
I want this method to return 1 if the user liked the page, or 0 if the user didn't.But it seems like the block is called before the function.
Is there a way to solve the problem?
Thank you!.
-(NSInteger) userWithId:(NSString *)fb_id likesPageWithID:(NSString *)page_id {
__block NSInteger returnValue;
NSString *fbGraphPath = [NSString stringWithFormat:@"/%@/likes/%@",fb_id,page_id];
NSLog(@"%@",fbGraphPath);
[FBRequestConnection startWithGraphPath:fbGraphPath parameters:nil HTTPMethod:@"GET" completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
NSLog(@"%@",result);
if(error) {
NSLog(@"%@",error);
} else {
if([result count] == 0) {
NSLog(@"%@",[result objectForKey:@"data"]);
returnValue = 0;
} else {
NSLog(@"%@",[result objectForKey:@"data"]);
returnValue = 1;
}
}
}];
return returnValue;
}
Upvotes: 2
Views: 173
Reputation: 572
I can think of three ways to do this:
Notification Center
Like Apurv answer suggests, you can use notification centre to broadcast the result:
- (void)userWithId:(NSString *)fb_id likesPageWithID:(NSString *)page_id {
__block NSInteger returnValue;
NSString *fbGraphPath = [NSString stringWithFormat:@"/%@/likes/%@",fb_id,page_id];
NSLog(@"%@",fbGraphPath);
[FBRequestConnection startWithGraphPath:fbGraphPath parameters:nil HTTPMethod:@"GET" completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
NSLog(@"%@",result);
if(error) {
NSLog(@"%@",error);
} else {
if([result count] == 0) {
NSLog(@"%@",[result objectForKey:@"data"]);
returnValue = 0;
} else {
NSLog(@"%@",[result objectForKey:@"data"]);
returnValue = 1;
}
}
[[NSNotificationCenter defaultCenter] postNotificationName:@"mynotif" object:nil userInfo:@{@"result":@(returnValue)}];
}];
}
Personally, I wouldn't do this unless I needed to broadcast the value. Using notifications adds some unnecessary overhead.
Condition
This is the simplest to implement, and it doesn't change who you use this method, but is a bit overkill.
- (NSInteger)userWithId:(NSString *)fb_id likesPageWithID:(NSString *)page_id {
__block NSInteger returnValue;
NSCondition *condition = [NSCondition new];
[condition lock];
NSString *fbGraphPath = [NSString stringWithFormat:@"/%@/likes/%@",fb_id,page_id];
NSLog(@"%@",fbGraphPath);
[FBRequestConnection startWithGraphPath:fbGraphPath parameters:nil HTTPMethod:@"GET" completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
NSLog(@"%@",result);
if(error) {
NSLog(@"%@",error);
} else {
if([result count] == 0) {
NSLog(@"%@",[result objectForKey:@"data"]);
returnValue = 0;
} else {
NSLog(@"%@",[result objectForKey:@"data"]);
returnValue = 1;
}
}
[condition lock];
[condition signal];
[condition unlock];
}];
[condition wait];
[condition unlock];
}
Also, this code will block the running thread. So the thread that calls this method can't be the same that runs the FB's completion handler.
Blocks
This is, in my opinion, the best.
- (void)userWithId:(NSString *)fb_id likesPageWithID:(NSString *)page_id withCompletionHandler:(void (^)(NSInteger))handler {
__block NSInteger returnValue;
NSString *fbGraphPath = [NSString stringWithFormat:@"/%@/likes/%@",fb_id,page_id];
NSLog(@"%@",fbGraphPath);
[FBRequestConnection startWithGraphPath:fbGraphPath parameters:nil HTTPMethod:@"GET" completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
NSLog(@"%@",result);
if(error) {
NSLog(@"%@",error);
} else {
if([result count] == 0) {
NSLog(@"%@",[result objectForKey:@"data"]);
returnValue = 0;
} else {
NSLog(@"%@",[result objectForKey:@"data"]);
returnValue = 1;
}
}
handler(returnValue);
}];
}
It's easy to use it to:
[var userWithId:fb_id likesPageWithID:page_id withCompletionHandler:^(NSInteger result) {
// use result for whatever you need
}];
Hope it helps.
Upvotes: 3
Reputation: 28766
Almost always the answer given by @Apurv will be correct, you'll do either of the following:
Though to answer your question directly, if you have an external library that provides asynchronous callbacks and you really would like to invoke it synchronously you can do the following:
Option 1: Use a semaphore
A semaphore halts execution until a signaled to proceed.
-(NSInteger) userWithId:(NSString *)fb_id likesPageWithID:(NSString *)page_id {
dispatch_semaphore_t querySemaphore = dispatch_semaphore_create(0);
__block NSInteger returnValue;
[FBRequestConnection startWithGraphPath:fbGraphPath parameters:nil HTTPMethod:@"GET" completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
returnValue = xyz; //calculate your return value
dispatch_semaphore_signal(querySemaphore);
}];
dispatch_semaphore_wait(querySemaphore, DISPATCH_TIME_FOREVER);
return returnValue;
}
Option 2: Use Runloop (hacky)
If your block is calling back on the same thread/queue that the dispatch was done from, a deadlock will occur. One way to avoid this is to use the run loop.
for (int i = 0; i < 5; i++) //time-out
{
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
if (returnValue)
{
return returnValue;
}
}
return nil;
Upvotes: 1
Reputation: 17186
Blocks work asynchronously. So, when you call startWithGraphPath
, the execution goes to the next line directly. Your completion block will be called once the caller method finishes its execution. So, in general you should not return the value. But at the end of block, you should broadcast the notification with proper value encapsulated in it.
Upvotes: 1