使用FacebookSDK从异步块返回值的方法 [英] Method returning value from asynchronous block with FacebookSDK

查看:164
本文介绍了使用FacebookSDK从异步块返回值的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要做的是Facebook iOS SDK的Facebook包装器。基本上我的想法是我的ViewController除了显示ex之外什么都不做。我的朋友将通过简单的电话获得,例如

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

我的包装myFriends方法应如下所示

  +(NSArray *)myFriends 
{
__block NSArray * friends = nil;
[FBSession openActiveSessionWithReadPermissions:无allowLoginUI:YES completionHandler:^(FBSession *会话,FBSessionState状态,NSError *错误){
如果(FB_ISSESSIONOPENWITHSTATE(状态)){
[FBRequestConnection startForMyFriendsWithCompletionHandler:^( FBRequestConnection * connection,id data,NSError * error){
CFRunLoopStop(CFRunLoopGetCurrent());
if(error){
return;
}
NSArray * friendsData =(NSArray *)[数据数据];
NSMutableArray * fbFriends = [NSMutableArray array];
for(friendData中的朋友数据){
朋友*朋友= [朋友friendWithDictionary:friendData];
fbFriends addObject:friend];
}
friends = [NSArray arrayWithArray:fbFriends];
}];
CFRunLoopRun();
}
}];
返回朋友;
}

问题是openActiveSessionWithReadPermissions和startForMyFriendsWithCompletionHandler是异步块,所以方法之前返回这些街区完成了他们的任务。



任何帮助都会非常感激。

解决方案

我过去创建了一个类似的包装器,我的方法是在调用我的包装器方法时传递一个完成块;一旦所有异步调用都完成运行,就会触发此完成块,并且它会接收您的方法在同步方案中返回的任何数据(在您的情况下,是朋友数组)。



为了说明 - 您可以将myFriends方法重新定义为:



+(void)myFriendsWithCompletionBlock:(void(^) (NSArray *朋友))completionBlock;



然后在实现中,紧跟在 friends = [NSArray arrayWithArray: fbFriends]; 一行,你会加上这个:

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

...并删除返回最后的声明。



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

  [FacebookWrapper myFriendsWithCompletionBlock:^(NSArray * friends){
//做你需要做的朋友数组
} ;;

当然,这仍然是异步的 - 但是没有办法解决,因为这就是Facebook的方式SDK是构建的(而且,公平地说,这可能是最好的方法 - 等待完成同步的请求会很糟糕!)



编辑:我注意到了如果失败,你也会从包装器方法返回;在这种情况下,你可以这样做而不是返回:

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

这将导致朋友 arra y当你的完成块被调用时, nil 然后你就可以在那里看到那个错误了。



<希望这有帮助!


What I am trying to do is a Facebook wrapper for the Facebook iOS SDK. Basically the idea is that my ViewController should do nothing but showing ex. my friends that will be acquired with a simple call like

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

My wrapper myFriends method should look like this

+ (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;
}

The issue is that the openActiveSessionWithReadPermissions and startForMyFriendsWithCompletionHandler are asynchronous blocks so the method returns before the blocks complete their task.

Any help would be much appreciated.

解决方案

I created a similar wrapper in the past and my approach was passing a "completion block" when calling my wrapper method; this completion block is then triggered once all the asynchronous calls are done running, and it receives whatever data your method would return in a synchronous scenario (in your case, the array of friends).

To illustrate - you could have your "myFriends" method redefined as:

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

Then in the implementation, right after the friends = [NSArray arrayWithArray:fbFriends]; line, you would add this:

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

... and remove the return statement at the end.

Finally, on your view controller (or any object using the method, you would do something like this:

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

Of course, this is still asynchronous - but there's no way around since that's how the Facebook SDK was build (and, to be fair, this is probably the best way to do it - waiting for requests to finish synchronous would be terrible!)

Edit: I noticed you're also returning from the wrapper method in case it fails; in that situation, instead of returning you would do something like this:

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

That would cause the friends array to be nil when your completion block is called - you can then treat that error there however seems appropriate to you.

Hope this helped!

这篇关于使用FacebookSDK从异步块返回值的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆