好的,我正在尝试做的事情:
我有一个由一些网络数据填充的模型对象,我希望能够像这样创建它:
// createWithID will fire off a request to a server
MyObject *newObject = [MyObject createWithID:123];
if(newObject){
//do something!
}
我遇到的问题是createWithID方法中的网络调用是异步的,因此该方法将始终在调用完成之前返回。
首先,这是一个可行的方法吗?我喜欢在方法中封装网络调用的想法。第二,可以在不阻塞主线程的情况下完成吗?
谢谢!
答案 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