方法使用FacebookSDK从asynchronous块返回值

我想要做的是Facebook的iOS SDK的Facebook包装。 基本上这个想法是,我的ViewController应该什么都不做,但显示前。 我的朋友,将通过一个简单的电话来获得

self.friends = [FacebookWrapper myFriends]; [self.tableView reloadData]; 

我的包装myFriends方法应该看起来像这样

 + (NSArray *)myFriends { __block NSArray *friends = nil; [FBSession openActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error) { if(FB_ISSESSIONOPENWITHSTATE(status)) { [FBRequestConnection startForMyFriendsWithCompletionHandler:^(FBRequestConnection *connection, id data, NSError *error) { CFRunLoopStop(CFRunLoopGetCurrent()); if(error) { return; } NSArray *friendsData = (NSArray *)[data data]; NSMutableArray *fbFriends = [NSMutableArray array]; for(id friendData in friendsData) { Friend *friend = [Friend friendWithDictionary:friendData]; fbFriends addObject:friend]; } friends = [NSArray arrayWithArray:fbFriends]; }]; CFRunLoopRun(); } }]; return friends; } 

问题是openActiveSessionWithReadPermissions和startForMyFriendsWithCompletionHandler是asynchronous块,所以在块完成任务之前,方法返回。

任何帮助将非常感激。

我在过去创build了一个类似的包装,我的方法是在调用我的包装方法时传递一个“完成块” 一旦所有的asynchronous调用都运行完毕,这个完成块就会被触发,并且它将接收到你的方法在同步场景中返回的任何数据(在你的情况下,是朋友数组)。

为了说明 – 你可以将你的“myFriends”方法重新定义为:

+ (void)myFriendsWithCompletionBlock:(void (^)(NSArray *friends))completionBlock;

然后在实现中,在friends = [NSArray arrayWithArray:fbFriends]; 行,你可以添加这个:

 if (completionBlock != nil) { completionBlock(friends); } 

…并在最后删除return语句。

最后,在您的视图控制器(或任何使用该方法的对象,你会做这样的事情:

 [FacebookWrapper myFriendsWithCompletionBlock:^(NSArray *friends){ // do what you need to do with the friends array }]; 

当然,这仍然是asynchronous的 – 但是没有办法解决这个问题,因为这就是Facebook SDK的构build方式(公平地说,这可能是最好的方法 – 等待请求完成同步将是可怕的!)

编辑:我注意到你也从包装方法返回的情况下,它失败了; 在这种情况下,不要回来,你会做这样的事情:

 if (completionBlock != nil) { completionBlock(nil); } 

当你的完成块被调用的时候,这会导致friends数组为nil – 然后你可以把那个错误看作是适合你的。

希望这有助于!

如果你正在调度一个asynchronous块,你可以通过调用它来与你的UIViewController子类通信:

 [self someSelectorWithCallbackData:stuffWhichYouWantToGiveBack]; 

这会叫self被块捕获,所以会按预期工作。 从相关的方法你可以刷新视图/重新加载tableview /跳舞夹具根据需要。

根据上下文,你可能需要__block作用域self ,例如

 __block UIViewController *bsself = self; 

但是如果你使用后者,小心避免一个保留循环(构build和分析工具很好的指出了这一点)。

认为你需要使用protol @class Webservice;

 @protocol WebserviceDelegate @optional -(void)webservice:(Webservice *)webservice didFetchPosts:(NSArray *)posts; -(void)webservice:(Webservice *)webservice didFetchComments:(NSArray *)comments forPostID:(NSString *)postID launchComments:(BOOL)launch; -(void)webservice:(Webservice *)webservice didLoginWithUser:(User *)user; -(void)webservice:(Webservice *)webservice didVoteWithSuccess:(BOOL)success forObject:(id)object direction:(BOOL)up; @end @interface Webservice : NSObject { __weak id <WebserviceDelegate> delegate; } //Delegate @property (weak) id <WebserviceDelegate> delegate; -(void)getHomepage { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NSURLResponse *response; NSError *error; // Create the URL Request NSMutableURLRequest *request = [Webservice NewGetRequestForURL:[NSURL URLWithString:@"https://www.hnsearch.com/bigrss"]]; // Start the request NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; //Handle response //Callback to main thread if (responseData) { NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSStringEncodingConversionAllowLossy]; if (responseString.length > 0) { dispatch_async(dispatch_get_main_queue(), ^{ [self parseIDsAndGrabPosts:responseString]; }); } else { dispatch_async(dispatch_get_main_queue(), ^{ [delegate webservice:self didFetchPosts:nil]; }); } } else { dispatch_async(dispatch_get_main_queue(), ^{ [delegate webservice:self didFetchPosts:nil]; }); } }); } -(void)parseIDsAndGrabPosts:(NSString *)parseString { // Parse String and grab IDs NSMutableArray *items = [@[] mutableCopy]; NSArray *itemIDs = [parseString componentsSeparatedByString:@"<hnsearch_id>"]; for (int xx = 1; xx < itemIDs.count; xx++) { NSString *idSubString = itemIDs[xx]; [items addObject:[idSubString substringWithRange:NSMakeRange(0, 13)]]; } // Send IDs back to HNSearch for Posts dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NSURLResponse *response; NSError *error; // Create Request String NSString *requestString = @"http://api.thriftdb.com/api.hnsearch.com/items/_bulk/get_multi?ids="; for (NSString *item in items) { requestString = [requestString stringByAppendingString:[NSString stringWithFormat:@"%@,", item]]; } // Create the URL Request NSMutableURLRequest *request = [Webservice NewGetRequestForURL:[NSURL URLWithString:requestString]]; // Start the request NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; //Handle response //Callback to main thread if (responseData) { NSArray *responseArray = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:&error]; if (responseArray) { NSMutableArray *postArray = [@[] mutableCopy]; for (NSDictionary *dict in responseArray) { [postArray addObject:[Post postFromDictionary:dict]]; } NSArray *orderedPostArray = [self orderPosts:postArray byItemIDs:items]; dispatch_async(dispatch_get_main_queue(), ^{ [delegate webservice:self didFetchPosts:orderedPostArray]; // Update Karma for User if ([HNSingleton sharedHNSingleton].User) { [self reloadUserFromURLString:[NSString stringWithFormat:@"https://news.ycombinator.com/user?id=%@", [HNSingleton sharedHNSingleton].User.Username]]; } }); } else { dispatch_async(dispatch_get_main_queue(), ^{ [delegate webservice:self didFetchPosts:nil]; }); } } else { dispatch_async(dispatch_get_main_queue(), ^{ [delegate webservice:self didFetchPosts:nil]; }); } }); }