创建一个对象,让它等到它被创建

时间:2012-04-07 01:44:33

标签: iphone objective-c grand-central-dispatch

好的,我正在尝试做的事情:

我有一个由一些网络数据填充的模型对象,我希望能够像这样创建它:

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

}

我遇到的问题是createWithID方法中的网络调用是异步的,因此该方法将始终在调用完成之前返回。

首先,这是一个可行的方法吗?我喜欢在方法中封装网络调用的想法。第二,可以在不阻塞主线程的情况下完成吗?

谢谢!

2 个答案:

答案 0 :(得分: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的指针。你无法获得一个并且完全可以使用它。虽然没有取消机制,但你仍然可以扩展它以实现这一点。

答案 1 :(得分: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