【问题标题】:Is my understanding of completionHandler blocks correct?我对 completionHandler 块的理解是否正确?
【发布时间】:2013-09-01 19:39:34
【问题描述】:

到目前为止,我已经阅读了很多关于块的信息,Apple 指南、Cocoabuilder 以及关于 SO 的 3 篇文章,并且我在代码中使用了基本上从在线教程中获得的示例。我仍在尝试理解一个具体问题。所以我决定制作一个应用程序,仅包含一个completionHandler 示例以更好地理解。这是我想出的:

视图控制器

- (void)viewDidLoad
[SantiappsHelper fetchUsersWithCompletionHandler:^(NSArray *users) {
    self.usersArray = [NSMutableArray array];

    for (NSDictionary *userDict in users) {
        [self.usersArray addObject:[userDict objectForKey:@"username"]];
    }

    //WHILE TESTING postarray method, comment this out...
    //[self getPoints];
    [self.tableView reloadData];

}];

}

SantiappsHelper.h/m

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


+(void)fetchUsersWithCompletionHandler:(Handler)handler {

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_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
//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;
            return;
        }
        NSLog(@"Error %@", error);
        return;
    }

    NSString *responseString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];

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

    if (handler){
        //dispatch_sync WAITS for the block to complete before returning the value
        //otherwise, the array is returned but gets zeroed out
        dispatch_sync(dispatch_get_main_queue(), ^{
        handler(usersArray);
        });
    }
});
}

这是我的理解......

  1. 我从我的 VC 中调用 fetchUsersWithCompletionHandler 并将这个完成块传递给它。该块采用 NSArray users 参数并返回 void。

  2. 同时,在 SantiappsHelper 类中,我们有一个 ^block 类型的 handler 变量,它从 VC 接收。

  3. fetchUsersWithCompletionHandler 方法运行,采用 CompletionBlock 参数,它本身采用 NSArray users 参数?有点混乱。

  4. webfetch 是 dispatch_async,所以它不会阻塞主线程。所以主线程上的执行继续。该新线程同步执行获取,新线程将停止直到返回响应。一旦新线程收到响应,它就会填写 NSHTTPURLResponse。返回后,它会使用 NSJSONSerialized 数据填充 usersArray。

  5. 最后它到达 if 测试并检查传入的 PARAMETER 处理程序是否存在?有点混乱……传入的参数是completionBlock。那个handler参数是不是一直存在并且明显存在,因为它被传入了?

  6. 一旦处理程序 !NULL 然后执行返回到主线程,将块期望的 NSArray 用户传回 VC 中。

但是,如果我将第二次调度更改为异步,则 usersArray 会正确填充,但是一旦将 handler(usersArray) 发送回主线程,它就会为空或为零!为什么?

【问题讨论】:

    标签: objective-c objective-c-blocks


    【解决方案1】:
    1. 正确。说/考虑这一点的最好方法是说您正在调用一个名为fetchUsersWithCompletionHandler: 的方法。这个方法会消失并做一些工作,在将来的某个时候它可能会执行你在块文字中声明的代码并传入一个用户数组。

    2. 该方法采用类型为void (^)(NSArray *users) 的名为handler 的参数。此类型表示一段代码,在调用时应接收并排列并且不返回任何结果。

    3. fetchUsersWithCompletionHandler: 做了一些工作,在某些时候可能会调用传入的块,并将用户数组作为块参数。

    4. 正确

    5. if (handler) { 检查处理程序参数是否不是nil。在大多数情况下,尤其是如果您始终使用块文字调用 fetchUsersWithCompletionHandler:但是您始终可以使用 [self fetchUsersWithCompletionHandler:nil]; 调用该方法,或者从其他地方传递一个变量来调用它作为完成,可能是nil。如果您尝试取消引用nil 来调用它,那么您将崩溃。

    6. 执行不会“传回”到主线程,您只是将要在主线程上执行的工作块排入队列。您正在使用 dispatch_sync 调用来执行此操作,这将阻塞此后台线程,直到阻塞完成 - 这并不是真正需要的。

    数组为nil 可能是您声明usersArray__block 存储的结果。这不是必需的,因为您在任何时候都没有修改 usersArray 指向的内容,您只是在其上调用方法。

    【讨论】:

    • 感谢您的耐心,我真的在这里尝试:)。好的,我现在明白,如果第一次分派是异步的,那么 GCD 块内的任何内容都会被启动,但会继续执行 if(handler) 测试,而无需等待。但是我有获取 Web 数据的代码,如果第二次调度是异步的,那么实际上是一个数组的 theString 会在返回原始调用方法时返回 nil。它不会从 webfetch JSONSerialization 返回 nil,这很好并且已填充。但是当我将它返回给 viewcontroller 方法时,该数组为 nil。我必须让它 dispatch_sync 才能工作……为什么?
    • 您能否用您正在谈论的特定代码更新您的问题,因为它可能更容易解释
    • 好吧,是什么使它成为一个完成块(一种仅在完成繁重的事情时才返回的方法)是因为我们将它发送到 GCD 队列,然后让它返回一次这是在不阻塞主线程的情况下完成的,不是因为实际的块具有等待直到某事完成的属性? Iow,是什么让完成处理程序在任务完成时返回取决于执行什么代码以及如何执行,这不是块所固有的?你可以有没有块的完成处理程序?
    • @marciokoko 是的,你是对的 - 没有什么能让它们特别的块。块恰好对这项任务很方便。在引入块之前,实现这一点的方式主要是通过委托 - 请查看 NSURLConnection 以获得一个很好的示例。
    • 好的,所以 GCD 负责等待它完成,然后执行“THIS”。 Blocks 负责以简洁的格式说出“THIS”实际上是什么?
    猜你喜欢
    • 2018-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-07
    • 2020-03-03
    • 2010-11-07
    • 1970-01-01
    相关资源
    最近更新 更多