3

私がやろうとしているのは、Facebook iOS SDK の Facebook ラッパーです。基本的には、ViewController は ex を表示するだけでよいという考えです。のような簡単な呼び出しで取得される私の友達

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 が非同期ブロックであるため、ブロックがタスクを完了する前にメソッドが戻ることです。

どんな助けでも大歓迎です。

4

3 に答える 3

3

私は過去に同様のラッパーを作成しましたが、私のアプローチは、ラッパーメソッドを呼び出すときに「完了ブロック」を渡すことでした。この完了ブロックは、すべての非同期呼び出しの実行が完了するとトリガーされ、同期シナリオでメソッドが返すデータ(この場合はフレンドの配列)を受け取ります。

説明のために、「myFriends」メソッドを次のように再定義することができます。

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

次に、実装では、行の直後にfriends = [NSArray arrayWithArray:fbFriends];次を追加します。

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

return...そして最後のステートメントを削除します。

最後に、View Controller(またはメソッドを使用するオブジェクト)で、次のようにします。

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

もちろん、これはまだ非同期です-しかし、Facebook SDKが構築された方法であるため、回避する方法はありません(そして、公平を期すために、これはおそらくそれを行うための最良の方法です-要求が同期を終了するのを待つのはひどいでしょう!)

編集:失敗した場合に備えて、ラッパーメソッドからも戻ってきていることに気づきました。そのような状況では、戻る代わりに、次のようなことをします。

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

これにより、完了ブロックが呼び出されたときにfriends配列が作成さnilれます。その後、そのエラーを処理できますが、適切と思われます。

これがお役に立てば幸いです。

于 2013-02-28T13:59:50.637 に答える
0

非同期ブロックをディスパッチしている場合は、UIViewControllerサブクラスにコールバックすることでサブクラスと通信できます。

[self someSelectorWithCallbackData:stuffWhichYouWantToGiveBack];

これはself、ブロックによってキャプチャされるように呼び出されるため、期待どおりに機能します。関連するメソッドから、必要に応じてビューを更新したり、テーブルビューをリロードしたり、ジグを踊ったりすることができます。

コンテキストによっては、__blockスコープが必要になる場合がありますself

__block UIViewController *bsself = self;

ただし、後者を実行する場合は、保持ループを避けるように注意してください (ビルドおよび分析ツールは、これを指摘するのにかなり適しています)。

于 2013-02-28T11:37:34.853 に答える