我有一个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。
答案 0 :(得分:4)
实际问题是"如何从异步方法返回结果?"
说,你有一个异步任务" doSomethingAsync" (这是类方法或实例方法或函数,但这并不重要)。
熟悉的同步表单" doSomething"只会返回结果,可以声明如下:
- (Result*) doSomething;
等效的异步任务" doSomethingAsync"可以使用完成处理程序声明:
typedef void (^completion_block_t)(Result* result)
- (void) doSomethingAsync:(completion_block_t)completionHandler;
示例:
假设一个班级" MyClass"定义一个属性"结果"这将从异步类方法(类Foo)的结果初始化。您可以在方法" fetchResult":
中检索结果- (void) fetchResult {
[Foo doSomethingAsync:^(Result* result){
self.result = result;
}];
}
可能需要一段时间才能掌握这里发生的事情,这需要你“思考异步”#34; ;)
要实现的重要一点是,完成处理程序是一个块 - 它被定义为内联并被视为普通对象。该块由调用站点创建,并作为参数 completionHandler 的参数传递给doSomethingAsync:
方法。块本身定义了异步任务完成时要采取的操作。
另一方面,异步方法在内部必须保持对该块的引用,直到它完成。然后,它必须调用块并将其结果作为参数提供给完成块的参数 result 。
还有其他形式的"返回"异步函数的结果。一种常见的模式是使用 Future 或 Promise 。 Future或Promise只表示异步函数的最终结果。它是一个对象,可以从异步函数立即返回 - 但它的值(异步任务的结果)仅在以后可用当异步任务完成时。任务必须在完成时最终为promise设置一个值。这被称为"解决"。这意味着,任务必须保持对返回的promise对象的引用,最后"解决"它的值为成功,或者值为 failure 。
假设有这样一个类" Promise",这可以让你声明这样的异步方法:
- (Promise*) doSomethingAsync;
Promise的实现可能完全支持"异步模型"。为了检索结果,您只需定义结果可用时要执行的操作。 Promise的特定实现可以实现此目的,例如:
- (void) fetchResult {
Promise* promise = [Foo doSomethingAsync];
promise.then(^(Result* result){
self.result = result;
});
}
注意"然后",它实际上是Promise类的属性,它返回块:
@property then_block_t then;
这个返回的类型" then_block_t"立即通过以下方式调用:
promise.then(...)
很像:
then_block_t block = promise.then;
block( ... );
但更短。
类型块" then_block_t"有一个参数是完成块,当结果最终可用时,它将由promise调用。完成块是内联定义的:
^(Result* result){ ... }
如您所见,完成块具有参数 result ,这是异步方法的实际结果。
好的,现在你的头可能会旋转;)
但现在回到示例
Promise* promise = [Foo doSomethingAsync];
promise.then(^(Result* result){
self.result = result;
});
简单地说:
"启动异步方法[Foo doSomethingAsync]并返回一个 诺言。
完成后,执行任务结果的块 " doSomethingAsync"使用参数 result 传递。"
你写得更短:
[Foo doSomethingAsync]
.then(^(Result* result) {
self.result = result;
};
类似于带有完成处理程序的表单:
[Foo doSomethingAsync:^(Result* result){
self.result = result;
}];
承诺最重要的特征是,它允许我们链接"两个或多个异步任务在一起。这是有可能的,因为从属性then_block_t
返回的类型then
的块具有类型Promise
的返回值。
typedef Promise* (^then_block_t)(completion_block_t onSuccess);
我很确定你的脑袋现在正以高频率旋转;) - 因此,一个例子可以清楚地说明这一点(希望如此):
假设您有两个异步方法:asyncA和asyncB。第一个需要输入,异步处理并产生结果。第二种方法asyncB应该采用这个结果,异步处理它,最后打印出来" OK"或NSError - 如果出现问题:
[self asyncA:input]
.then(^(OutputA* outA) {
return [self asyncB:outA];
})
.then(^(OutputB* outB){
NSLog(@"end result: %@", outB);
return nil;
});
这是:
"异步执行任务" asyncA"。
完成后,异步执行任务" asyncB"。
如果完成,则然后打印出结果。"
您可能会注意到处理程序将在语句
中返回 Promise 对象 return [self asyncB:outA];
。
这将建立"链"表格任务" asyncA" to" asyncB"。最终的价值"然后,返回的promise将在下一个处理程序中显示为 result 参数。
处理程序也可能返回立即结果,该结果恰好也会成为下一个处理程序中的 result 参数。
Objective-C中的实际实现略有不同,因为* then_block_t *具有两个参数:一个用于成功案例,一个用于失败案例:< / p>
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;
});
承诺的另一个很酷的功能是错误将通过承诺链转发。这意味着,一个人可以拥有多个&#34;链接&#34;任务说,A,B,C,D,其中只定义了成功处理程序。最后一个处理程序(对)定义了错误处理程序。如果第一个异步任务中发生错误 - 该错误将通过所有promise转发,直到错误处理程序最终处理它为止。只有在任务成功时才会调用成功处理程序,并且只有在任务失败时才会调用错误处理程序:
[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;
});
还有关于Promises的更多信息,但远远超出了这个答案。
可以在此处找到实施示例:RXPromise
答案 1 :(得分:1)
我建议如下:
将以下内容添加到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;
我认为应该这样做。另外,如果需要,在主线程上执行处理程序块。