如何从Helper类返回值到视图控制器?

我有一个viewcontroller,在它的viewDidLoad中调用一个HelperClass类的方法,像这样:

- (void)viewDidLoad{ [super viewDidLoad]; self.usersArray = [SantiappsHelper fetchUsers]; } 

该类的方法如下所示:

 +(NSArray *)fetchUsers{ NSString *urlString = [NSString stringWithFormat:@"http://www.myserver.com/myApp/getusers.php"]; NSURL *url = [NSURL URLWithString:urlString]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10]; [request setHTTPMethod: @"GET"]; __block NSArray *usersArray = [[NSArray alloc] init]; dispatch_async(dispatch_get_main_queue(), ^{ // Peform the request NSURLResponse *response; NSError *error = nil; NSData *receivedData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; if (error) { // Deal with your error if ([response isKindOfClass:[NSHTTPURLResponse class]]) { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response; NSLog(@"HTTP Error: %d %@", httpResponse.statusCode, error); return; } NSLog(@"Error %@", error); return; } NSString *responseString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding]; NSLog(@"responseString fetchUsers %@", responseString); NSLog(@"inside of block"); usersArray = [NSJSONSerialization JSONObjectWithData:[responseString dataUsingEncoding:NSASCIIStringEncoding] options:0 error:nil]; }); NSLog(@"outside of block"); return usersArray; 

}

responseString打印出来就好了。 但是,如何将该值返回给我的视图控制器? 因为它是一个tableview控制器,在获得任何数据之前已经加载了它的tableview。

实际的问题是“如何从asynchronous方法返回结果?”

说,你有一个asynchronous任务“doSomethingAsync”(这是一个类方法或实例方法或函数,但这并不重要)。

熟悉的同步forms “doSomething”将简单地返回结果并可以声明如下:

- (Result*) doSomething;

可以使用完成处理程序来声明等效的asynchronous任务“doSomethingAsync”:

 typedef void (^completion_block_t)(Result* result) - (void) doSomethingAsync:(completion_block_t)completionHandler; 

例:

假设一个“MyClass”类定义了一个属性“result”,它将从asynchronous类方法(Foo类)的结果中初始化。 您可以在“fetchResult”方法中检索结果:

 - (void) fetchResult { [Foo doSomethingAsync:^(Result* result){ self.result = result; }]; } 

这可能需要一段时间才能掌握,这需要你“思考asynchronous”;)

要认识到的重要一点是,完成处理程序是一个块 – 它是内联定义的,并被当作普通对象处理。 该块由调用站点创build,并作为参数completionHandler的parameter passing给doSomethingAsync:方法。 块本身定义了asynchronous任务完成时要执行的操作。

另一方面,asynchronous方法在内部必须保持对该块的引用,直到完成。 然后,它必须调用该块并将其结果作为参数提供给完成块的参数结果


还有其他的forms来“返回”一个asynchronous函数的结果。 一个常见的模式是使用未来承诺 。 Future或Promise只是表示asynchronous函数的最终结果。 它是一个可以立即从asynchronous函数返回的对象 – 但是它的 (asynchronous任务的结果)仅在稍后asynchronous任务完成时才可用。 任务最终必须在完成时为承诺设定一个值。 这就是所谓的“解决”。 这意味着,任务必须保留对返回的承诺对象的引用,最后用值意味着成功或价值意义上的失败来 “解决”它。

假设有这样一个类“Promise”,这可以让你声明这样的asynchronous方法:

- (Promise*) doSomethingAsync;

Promise的实现可能完全支持“asynchronous模型”。 为了检索结果,只需定义结果可用时要执行的操作。 Promise的特定实现可以完成这个例如:

 - (void) fetchResult { Promise* promise = [Foo doSomethingAsync]; promise.then(^(Result* result){ self.result = result; }); } 

注意“then”,它实际上是Promise类的一个属性 ,返回一个

 @property then_block_t then; 

返回的types为“then_block_t”的块将立即通过以下方式调用:

 promise.then(...) 

很像:

 then_block_t block = promise.then; block( ... ); 

但更短。

types为“then_block_t”的块具有参数,该参数是当结果最终可用时将由promise调用的完成块 。 完成块被内联定义:

 ^(Result* result){ ... } 

正如你所看到的, 完成块有一个参数结果 ,这是asynchronous方法的实际结果。

好吧,现在你的头可以旋转;)

但现在回到这个例子

  Promise* promise = [Foo doSomethingAsync]; promise.then(^(Result* result){ self.result = result; }); 

简单地说:

  • “启动asynchronous方法[Foo doSomethingAsync]并返回一个承诺。

  • 完成后,执行任务“doSomethingAsync”的结果以参数结果传递的块。

你可以写得更短:

 [Foo doSomethingAsync] .then(^(Result* result) { self.result = result; }; 

类似于完成处理程序的forms:

 [Foo doSomethingAsync:^(Result* result){ self.result = result; }]; 

Promise最重要的特性就是它允许我们将两个或更多asynchronous任务“链接”在一起。 这是可能的,因为从属性返回的then_block_ttypes的块具有typesPromise的返回值。

typedef Promise* (^then_block_t)(completion_block_t onSuccess);

我很确定你的脑袋正在高频旋转;) – 因此,一个例子将使这个清楚(希望):

假设你有两个asynchronous方法:asyncA和asyncB。 第一个需要input,asynchronous处理并产生结果。 第二种方法asyncB应该得到这个结果,asynchronous处理,最后打印出“OK”或NSError – 如果出错了:

 [self asyncA:input] .then(^(OutputA* outA) { return [self asyncB:outA]; }) .then(^(OutputB* outB){ NSLog(@"end result: %@", outB); return nil; }); 

这写道:

  • “asynchronous执行任务”asyncA“。

  • 完成后,asynchronous执行任务“asyncB”。

  • 如果完成, 打印出结果。“

您可能会注意到处理程序将在语句中返回一个Promise对象

return [self asyncB:outA];

这将build立“连锁”forms的任务“asyncA”到“asyncB”。 返回的promise的最终“值”将作为下一个处理程序中的结果参数出现。

一个处理程序也可以返回一个直接的结果,这个结果恰好在下一个处理程序中作为结果参数。


Objective-C中的实际实现略有不同,因为* then_block_t *有两个参数:一个用于成功案例,另一个用于失败案例:

typedef Promise* (^then_block_t)(completion_block_t onSuccess, failure_block_t onFailure);

为了简洁起见,我在前面的样本中留下了这一点。 一个实际的实现将如下所示:

 [self asyncA:input] .then(^(OutputA* out) { return [self asyncB:out]; }, nil) .then(^(id result){ NSLog(@"result: %@", result); return nil; }, ^id(NSError*error){ NSLog(@"ERROR: %@", error); return nil; }); 

承诺的另一个很酷的特点是错误将通过承诺链转发。 这意味着,可以有多个“链接”任务,A,B,C,D,其中只定义了成功处理程序。 最后一个处理器(pair)定义了一个error handling器。 如果在第一个asynchronous任务中发生错误,那么将通过所有的承诺来转发错误,直到error handling程序最终处理它为止。 成功处理程序只有在任务成功时才会被调用,并且只有在任务失败时才会调用error handling程序:

 [self A] .then(^(id result) { return [self B:result]; }, nil) .then(^(id result) { return [self C:result]; }, nil) .then(^(id result) { return [self D:result]; }, nil) .then(^(id result) { NSLog(@"Success"); return nil; }, ^id(NSError*error){ NSLog(@"ERROR: %@", error); return nil; }); 

还有更多的承诺,但远远超出这个答案。

一个实现的例子可以在这里find: RXPromise

我会build议如下:

将以下内容添加到SantiappsHelper.h

 typedef void (^Handler)(NSArray *users); 

在viewDidLoad中,更改

 self.usersArray = [SantiappsHelper fetchUsers]; 

 [SantiappsHelper fetchUsersWithCompletionHandler:^(NSArray *users) { self.usersArray = users; }]; 

更改

 +(NSArray *)fetchUsers{ 

 +(void)fetchUsersWithCompletionHandler:(Handler)handler { 

在.m和.h文件中。

而在这个方法之后

 usersArray = [NSJSONSerialization JSONObjectWithData:[responseString dataUsingEncoding:NSASCIIStringEncoding] options:0 error:nil]; 

 if (handler) handler(usersArray); 

去掉

 return usersArray; 

我认为应该这样做。 另外,如果需要,在主线程上执行处理程序块。