如何将值从Helper类返回到视图控制器? [英] How do I return a value from a Helper Class to a view controller?

查看:60
本文介绍了如何将值从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(),^ {//执行请求NSURLResponse *响应;NSError *错误=无;NSData * receivedData = [NSURLConnection sendSynchronousRequest:requestreturningResponse:& response错误:&错误];如果(错误){//处理您的错误如果([response isKindOfClass:[NSHTTPURLResponse类]]){NSHTTPURLResponse * httpResponse =(NSHTTPURLResponse *)响应;NSLog(@"HTTP错误:%d%@",httpResponse.statusCode,错误);返回;}NSLog(@错误%@",错误);返回;}NSString * responseString = [[[NSString分配] initWithData:receivedData编码:NSUTF8StringEncoding];NSLog(@"responseString fetchUsers%@",responseString);NSLog(@"inside of block");usersArray = [NSJSONSerialization JSONObjectWithData:[responseString dataUsingEncoding:NSASCIIStringEncoding] options:0 error:nil];});NSLog(@块外");返回usersArray; 

}

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

解决方案

实际问题是异步方法如何返回结果?"

说,您有一个异步任务"doSomethingAsync"(它是类方法,实例方法或函数,但这并不重要).

熟悉的同步形式"doSomething"会简单地返回结果,并且可以这样声明:

-(Result *)doSomething;

等效的异步任务"doSomethingAsync"可以使用完成处理程序声明:

  typedef void(^ completion_block_t)(结果*结果)-(void)doSomethingAsync:(completion_block_t)completionHandler; 

示例:

假设类"MyClass"定义了一个属性结果",该结果将从异步类方法(类Foo)的结果中初始化.您可以在方法"fetchResult"中检索结果:

 -(无效)fetchResult {[Foo doSomethingAsync:^(结果*结果){self.result =结果;}];} 

可能要花一点时间来了解这里发生的事情,这需要您思考异步";)

要意识到的重要一点是,完成处理程序是一个块-内联定义 ,并且将其视为普通对象.该块由调用站点创建,并作为参数 completionHandler 的参数传递给 doSomethingAsync:方法.块本身定义了异步任务完成时要采取的动作.

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


还有其他形式可以返回"异步函数的结果.一种常见的模式是使用 Future Promise .Future或Promise只是表示异步函数的最终结果.它是一个对象,可以从异步函数中立即返回-但其 value (异步任务的结果)仅在以后可用当异步任务完成时.任务必须在完成时最终为诺言设置一个值.这称为解决".这意味着,该任务必须保留对返回的promise对象的引用,并最终使用"成功"或"失败"的值解析"它.

假设存在此类"Promise",这将使您可以声明如下异步方法:

-(Promise *)doSomethingAsync;

Promise的实现可能完全支持异步模型".为了检索结果,您只需定义可用结果时的操作.Promise的特定实现可以例如通过以下方式实现:

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

注意"then",实际上是Promise类的属性,该属性返回 block :

  @property then_block_t然后; 

此返回的类型为"then_block_t"的块将通过以下方式立即调用:

  promise.then(...) 

很像:

  then_block_t块= promise.then;堵塞( ... ); 

但较短.

类型为"then_block_t"的块具有一个参数,该参数是完成块,当结果最终可用时,promise将调用该参数.内嵌定义了完成块:

  ^(结果*结果){...} 

如您所见,补全块具有参数 result ,这是异步方法的实际结果.

好的,现在你的头可能会旋转;)

但是现在,回到示例

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

其简单地显示为:

  • "启动异步方法[Foo doSomethingAsync]并返回一个承诺.

  • 完成后 then 执行任务结果所在的块"doSomethingAsync"与参数 result 一起传递."

您可以写得更短:

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

类似于带有完成处理程序的表单:

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


不过,Promise的最重要特征是,它允许我们将两个或多个异步任务链接"在一起.之所以可行,是因为从属性 then 返回的类型为 then_block_t 的块的返回值为类型为 Promise

.

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

我很确定你的头现在正在高频旋转;)-因此,一个例子可以使这一点变得清晰(希望如此):

假设您有两个异步方法:asyncA和asyncB.第一个需要输入,对其进行异步处理并产生结果.第二种方法asyncB应该获取此结果,对其进行异步处理,最后打印出@"OK"或NSError-如果出现问题:

  [自身asyncA:input].then(^(OutputA * outA){返回[self asyncB:outA];}).then(^(OutputB * outB){NSLog(@最终结果:%@",outB);返回零;}); 

内容为:

  • 异步执行任务" asyncA.

  • 完成后,然后异步执行任务"asyncB".

  • 完成后,然后打印出结果."

您可能会注意到处理程序将在语句

中返回 Promise 对象

返回[自身asyncB:outA]; .

这将建立链"形式的任务"asyncA"到"asyncB".然后,返回的Promise的最终值"将在下一个处理程序中显示为 result 参数.

处理程序还可能会返回一个 immediate 结果,该结果恰好也是下一个处理程序中的 result 参数.


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

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

为简洁起见,我在以前的示例中忽略了这一点.实际的实现如下所示:

  [自身asyncA:input].then(^(OutputA * out){返回[self asyncB:out];},无).then(^(id result){NSLog(@结果:%@",结果);返回零;},^ id(NSError * error){NSLog(@错误:%@",错误);返回零;}); 


promise的另一个很酷的功能是,错误将通过promise链转发.这意味着,可以有多个链接"任务,例如A,B,C,D,其中仅定义了成功处理程序.最后一个处理程序(对)定义一个错误处理程序.如果在第一个异步任务中发生错误-该错误将通过所有promise转发,直到错误处理程序最终对其进行处理为止.仅当任务成功时才调用成功处理程序,而仅当任务失败时才调用错误处理程序:

  [自身A].then(^(id result){返回[自我B:结果];},无).then(^(id result){返回[self C:result];},无).then(^(id result){返回[自身D:结果];},无).then(^(id result){NSLog(@成功");返回零;},^ id(NSError * error){NSLog(@错误:%@",错误);返回零;}); 

关于Promises的更多信息,但远远超出了SO的答案.

可以在此处找到实现示例: RXPromise

I have a viewcontroller that calls a HelperClass class method in its viewDidLoad like so:

- (void)viewDidLoad{
    [super viewDidLoad];

    self.usersArray = [SantiappsHelper fetchUsers];    
}

That class method looks like this:

+(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;

}

The responseString is printed out just fine. But how do I return that value to my view controller? Because its a tableview controller which already loads its tableview before any of the data is gotten.

解决方案

The actual question is "How is a result returned from an asynchronous method?"

Say, you have an asynchronous task "doSomethingAsync" (which is a class method or instance method or a function, but this doesn't really matter).

The familiar synchronous form "doSomething" would simply return the result and can be declared as follows:

- (Result*) doSomething;

The equivalent asynchronous task "doSomethingAsync" can be declared using a completion handler:

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

Example:

Suppose a class "MyClass" defines a property "result" which will be initialized from the result of an asynchronous class method (class Foo). You retrieve the result in the method "fetchResult":

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

It may take a while to grasp what's going on here and it requires you to "think asynchronous" ;)

The important thing to realize is, that the completion handler is a block - which is defined inline and treated as if it were a normal object. The block is created by the call site and passed as an argument for the parameter completionHandler to the doSomethingAsync: method. The block itself defines the actions to take when the asynchronous task completes.

On the other hand, the asynchronous method internally must keep a reference to this block until it completes. Then, it must call the block and provide its result as an argument to the parameter result of the completion block.


There are other forms to "return" the result of an asynchronous function. One common pattern is to use a Future or a Promise. A Future or Promise simply represents the eventual result of an asynchronous function. It is an object that can be immediately returned from the asynchronous function - but its value (the result of the asynchronous task) is available only later when the asynchronous task finished. The task MUST eventually set a value for the promise when it finishes. This is called "resolving". That means, that the task must keep a reference to that returned promise object, and finally "resolve" it with either a value meaning success or a value meaning failure.

Assuming there is such a class "Promise", this would let you declare asynchronous methods like this:

- (Promise*) doSomethingAsync;

An implementation of a Promise may be fully support the "asynchronous model". In order to retrieve the result, you simply define what to do when the result is available. A particular implementation of a Promise may accomplish this for example:

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

Notice the "then", which is actually a property of the class Promise that returns a block:

@property then_block_t then;

This returned block of type "then_block_t" is immediately called via:

promise.then(...)  

Much like:

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

but shorter.

The block of type "then_block_t" has a parameter which is a completion block which will be invoked by the promise when the result is eventually available. The completion block is defined inline:

^(Result* result){ ... }

As you can see, the completion block has a parameter result which is the actual result of the asynchronous method.

OK, now your head may spin ;)

But now, go back to the example

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

which simply reads:

  • "Start asynchronous method [Foo doSomethingAsync] and return a promise.

  • When finished then execute the block where the result of task "doSomethingAsync" is passed with the argument result."

You can it write even shorter:

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

which is akin to the form with the completion handler:

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


The most important feature of a Promise, though, is that it allows us to "chain" two or more asynchronous tasks together. This is made possible since the block of type then_block_t which is returned from the property then has a return value of type Promise.

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

I'm pretty sure your head is spinning now in high frequency ;) - thus, an example will make this clear (hopefully):

Suppose you have two asynchronous methods: asyncA and asyncB. The first requires an input, processes it asynchronously and produces a result. The second method asyncB should take this result, process it asynchronously and finally print out @"OK" or an NSError - if something went wrong:

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

This reads:

  • "Asynchronously perform task "asyncA".

  • When finished then asynchronously perform task "asyncB".

  • If finished, then print out result."

You may notice that a handler will return a Promise object in the statement

return [self asyncB:outA];.

This will establish the "chain" form task "asyncA" to "asyncB". The eventual "value" of the returned promise will then appear as the result parameter in next handler.

A handler may also return an immediate result which happens to end up as the result parameter in the next handler as well.


An actual implementation in Objective-C differs slightly, in that the *then_block_t* has two parameters: one for the success case and one for the failure case:

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

I left this out in the former samples for brevity. An actual implementation would look like this:

[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;
});


Anther cool feature of promises is that errors will be forwarded through the chain of promises. That means, one can have multiple "chained" tasks say, A, B, C, D where only the success handler is defined. The last handler (pair) defines an error handler. If an error occurs in the first async task - that error will be forwarded through all promises until an error handler finally handles it. Success handlers will only be called when the task succeeded, and the error handler will only be called when the task failed:

[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;
});

There's more regarding Promises, but well beyond this SO answer.

An example for an implementation can be found here: RXPromise

这篇关于如何将值从Helper类返回到视图控制器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆