如何将Helper Class中的值返回给视图控制器?

时间:2013-07-02 02:53:31

标签: ios grand-central-dispatch

我有一个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。

2 个答案:

答案 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;

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