我是来自EmberJS的JS背景的iOS开发新手。我想将我的EmberJS应用程序移植到iOS应用程序。因此,我想在我的iOS应用程序中使用类似的结构。由于EmberJS大量使用承诺,我搜索了类似于iOS的东西,偶然发现了ReactiveCocoa。 在ReactiveCocoa的介绍中说,这个框架可以用来实现Promises。我试了但是它没有正常工作。我想从一个非常简单的例子开始:
我想这样做,因为我必须在数据加载成功后执行几项操作。 我的方法基本上是,但我遇到了以下问题:
subscribeCompleted
中的日志语句。但是TableView保持空白。我怀疑这可能发生,因为我在后台线程中获取数据。 我认为承诺(subscribeCompleted
)的决心也可能在后台线程中发生,而Cocoa Touch可能不喜欢这样。我是对的吗?但如果是这种情况,我该如何实施承诺?
我希望你能帮助我开始使用ReactiveCocoa。谢谢! : - )
更新
我设法通过将reloadData
包裹在dispatch_async(dispatch_get_main_queue(), ^{...
中来解决这个问题但是我仍然不确定这是最好的方法还是ReactiveCocoa的建议。 所以我仍然热衷于听取一些答案 :-)
// this method wants to use the promise
- (void) loadDataAndPerformActionsAfterwards{
RACSignal *signal = [self fetchObjects];
[signal subscribeCompleted:^{
NSLog(@"Entered subscribeCompleted block signal!");
NSLog(@"Number of objects: %i", self.objects.count);
[self.tableView reloadData];
}];
}
// this method returns a promise. I omitted some parts but it shows basically how i go about resolving the promise.
- (RACSignal*) fetchMoviesForCurrentFormState{
return [RACSignal createSignal:^RACDisposable*(id<RACSubscriber> subscriber) {
NSLog(@"RAC createSignal Block called");
NSString *requestURL = @"...";
NSURL *urlObj = [NSURL URLWithString: requestURL];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData* data = [NSData dataWithContentsOfURL: urlObj];
if(data){
[self performSelectorOnMainThread:@selector(fetchedData:)
withObject:data waitUntilDone:YES];
[subscriber sendCompleted];
}else{
// Not implemented yet: handle the error case
[subscriber sendCompleted];
}
});
// actually i do not know yet what i should return here. Copied from a basic example.
return nil;
}];
}
答案 0 :(得分:3)
你是对的,这是线程问题。但是,您无需降低到GCD级别。
信号可以“传递”到另一个线程上,该线程只调用那里的任何订阅回调:
- (void) loadDataAndPerformActionsAfterwards {
[[[self
fetchObjects]
deliverOn:RACScheduler.mainThreadScheduler]
subscribeCompleted:^{
NSLog(@"Entered subscribeCompleted block signal!");
NSLog(@"Number of objects: %i", self.objects.count);
[self.tableView reloadData];
}];
}
答案 1 :(得分:1)
您可以查看RXPromise。它是Promises/A+ specification的Objective-C实现,具有更多功能。 (我是作者)。
使用RXPromise库的解决方案如下所示:
- (void) loadDataAndPerformActionsAfterwards {
[self fetchMovie]
.thenOn(dispatch_queue_get_main(), ^id(id fetchedMovie) {
self.model = fetchedObjects;
[self.tableView reloadData];
}, nil);
}
这假定,方法fetchMovie
返回Promise。
你怎么得到这个?好吧,您可以轻松地将任何异步方法或操作包装到返回Promise的方法或操作中。这适用于任何信号方法:完成块,回调函数,委托,KVO,通知等。
例如,NSURLConnection
的异步便利类方法的简化实现(在实践中,您应该检查响应并执行更好的错误处理):
- (RXPromise*) fetchMovie {
RXPromise* promise = [[RXPromise alloc] init];
NSMutableRequest* request = ...;
[NSURLConnection sendAsynchronousRequest:request
queue:networkQueue
completionHandler:^(NSURLResponse* response, NSData* data, NSError* error){
if (error) {
[promise rejectWithReason:error];
}
else {
[promise fulfillWithValue:data];
}
}];
return promise;
}
您可能希望使用NSURLConnection
委托的方法,或使用NSOperation
子类的方法。这使您可以实现取消:
- (RXPromise*) fetchObjects {
RXPromise* promise = [[RXPromise alloc] init];
NSMutableRequest* request = ...;
HTTPOperation* op =
[[HTTPOperation alloc] initWithRequest:request
queue:networkQueue
completionHandler:^(NSURLResponse* response, NSData* data, NSError* error){
if (error) {
[promise rejectWithReason:error];
}
else {
[promise fulfillWithValue:data];
}
}];
promise.then(nil, ^id(NSError* error){
[op cancel];
return nil;
});
[op start];
return promise;
}
这里,HTTPOperation对象将听取自己对错误信号的承诺。如果它收到一个,例如从另一个对象发送到promise的取消消息,则处理程序然后将cancel
消息“转发”到该操作。
例如,View Controller现在可以取消正在运行的HTTPOperation,如下所示:
- (void) viewWillDisappear:(BOOL)animate {
[super viewWillDisappear:animate];
[self.fetchObjectsPromise cancel];
self.fetchObjectPromise = nil;
}