0

さて、ここで私がやろうとしていることは次のとおりです。

いくつかのネットワーク データが取り込まれたモデル オブジェクトがあり、次のように作成できるようにしたいと考えています。

// createWithID will fire off a request to a server 
MyObject *newObject = [MyObject createWithID:123];
if(newObject){
   //do something!

}

私が抱えている問題は、 createWithID メソッド内のネットワーク呼び出しが非同期であるため、呼び出しが完了する前にメソッドが常に返されることです。

まず、これはこれを行う良い方法ですか?メソッドでネットワーク呼び出しをカプセル化するというアイデアが気に入っています。第二に、メインスレッドをブロックせずに実行できますか?

ありがとう!

4

2 に答える 2

3

@danhの答えを少し拡張するために、すべてが完了するまで、オブジェクトへのポインタを使用しないでください。例えば:

[MyObject makeObjectWithID:123 completion:^(MyObject *object, NSError *error) {
  if (object == nil) {
    NSLog(@"error creating object: %@", error);
  } else {
    NSLog(@"created object: %@", object);
  }
}];

そして、作成方法は次のようになります。

+ (void)makeObjectWithID:(NSInteger)objectID completion:(void(^)(MyObject*,NSError*))handler {
  handler = Block_copy(handler);
  dispatch_async(dispatch_get_global_queue(0,0), ^{
    MyObject *object = [[MyObject alloc] initWithID:objectID];

    NSError *error = nil;
    BOOL succeeded = [object doTheExpensiveAndBlockingSetupThingWithError:&error];

    if (succeeded == NO) {
      [object release], object = nil;
    }

    dispatch_async(dispatch_get_main_queue(), ^{
      handler([object autorelease], error);
    });
  });
  Block_release(handler);
}

原則として、APIを安全に作成すればするほど、何かを台無しにする可能性は低くなります。部分的に構築されたへのポインタがないため、この方法は安全ですMyObject。あなたがそれを手に入れて、それを完全に使用する準備ができていないようにする方法はありません。確かにキャンセルメカニズムはありませんが、それを可能にするためにこれを拡張することもできます。

于 2012-04-07T03:21:16.993 に答える
2

私があなたを理解している場合、MyObjectはセットアップを完了するために非同期リクエストを行っているため、呼び出し元は、非同期セットアップが完了するまで、作成したインスタンスの準備ができていると想定できません。

これを解決する方法は、呼び出し元にセットアップが完了したことを知らせることです。呼び出し元を MyObject のデリゲートにする、または完了時に MyObject に NSNotification をポストさせるなど、いくつかの優れたアプローチがありますが、呼び出し元のコードが読みやすくなることが多いため、ブロックを好む傾向があります。

それを行う方法は次のようになります... MyObject.h:

typedef void (^CompletionBlock)(id result, NSError *error);

+ (MyObject *)createWithId:(NSInteger)anId completion:(CompletionBlock)completion;

私のオブジェクトは、必要に応じて (@property(copy...) を使用して) ブロックを ivar として保持し、セットアップが完了したときにそれを呼び出すことができます。

呼び出し元は次のようになります。

// do something to indicate activity, like show an activity indicator
MyObject *newObject = [MyObject createWithID:123 completion::^(id r, NSError *e) {
    // hide the activity indicator
    if (!error) {
        // code here gets executed when newObject is ready... update the ui accordingly
    }
}];

// code here gets executed right away, and should not assume that newObject is ready
于 2012-04-07T02:54:00.860 に答える