如何从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_t
types的块具有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;
我认为应该这样做。 另外,如果需要,在主线程上执行处理程序块。