当您感兴趣的任务完成时,是什么让完成处理程序执行该块?

2024-05-20

我一直在询问并试图了解完成处理程序是如何工作的。我用过很多,也读过很多教程。我将在这里发布我使用的代码,但我希望能够创建自己的代码,而无需使用其他人的代码作为参考。

我理解这个完成处理程序,其中调用者方法:

-(void)viewDidLoad{
[newSimpleCounter countToTenThousandAndReturnCompletionBLock:^(BOOL completed){
        if(completed){ 
            NSLog(@"Ten Thousands Counts Finished");
        }
    }];
}

然后在被调用的方法中:

-(void)countToTenThousandAndReturnCompletionBLock:(void (^)(BOOL))completed{
    int x = 1;
    while (x < 10001) {
        NSLog(@"%i", x);
        x++;
    }
    completed(YES);
}

然后我根据许多 SO 帖子想出了这个:

- (void)viewDidLoad{
    [self.spinner startAnimating];
    [SantiappsHelper fetchUsersWithCompletionHandler:^(NSArray *users) {
        self.usersArray = users;
        [self.tableView reloadData];
    }];
}

调用此方法后,它将使用接收到的数据重新加载表视图:

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

+(void)fetchUsersWithCompletionHandler:(Handler)handler {
    NSURL *url = [NSURL URLWithString:@"http://www.somewebservice.com"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10];
    [request setHTTPMethod: @"GET"];
    **// We dispatch a queue to the background to execute the synchronous NSURLRequest**
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        // Perform the request
        NSURLResponse *response;
        NSError *error = nil;
        NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
                                                     returningResponse:&response
                                                                 error:&error];
        if (error) { **// If an error returns, log it, otherwise log the response**
            // 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;
        }
        **// So this line won't get processed until the response from the server is returned?**
        NSString *responseString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];

        NSArray *usersArray = [[NSArray alloc] init];
        usersArray = [NSJSONSerialization JSONObjectWithData:[responseString dataUsingEncoding:NSASCIIStringEncoding] options:0 error:nil];
        // Finally when a response is received and this line is reached, handler refers to the block passed into this called method...so it dispatches back to the main queue and returns the usersArray
        if (handler){
            dispatch_sync(dispatch_get_main_queue(), ^{
            handler(usersArray);
            });
        }
    });
}

我可以在计数器示例中看到,被调用的方法(带有传递的块)在完成之前永远不会退出循环。因此,“完成”部分实际上取决于被调用方法内的代码,而不是传递给它的块?

在这种情况下,“完成”部分取决于对 NSURLRequest 的调用是同步的这一事实。如果是异步的怎么办?我怎样才能推迟调用该块,直到我的数据被 NSURLResponse 填充?


您的第一个示例是正确且完整的,是理解完成块的最佳方法。他们没有更多的魔法。它们永远不会自动被执行。当某些代码调用它们时,它们就会被执行。

正如您所注意到的,在后一个示例中,很容易在正确的时间调用完成块,因为一切都是同步的。如果是异步的,那么您需要将块存储在实例变量中,并在异步操作完成时调用它。您可以安排在操作完成时收到通知(可能使用its完成处理程序)。

在 ivar 中存储块时请务必小心。您的示例之一包括:

   self.usersArray = users;

致电给self将导致块保留self(调用对象)。这可以很容易地创建一个保留循环。通常,您需要弱引用self像这样:

- (void)viewDidLoad{
  [self.spinner startAnimating];
  __weak typeof(self) weakSelf = self;
  [SantiappsHelper fetchUsersWithCompletionHandler:^(NSArray *users) {
    typeof(self) strongSelf = weakSelf;
    if (strongSelf) {
      [strongSelf setUsersArray:users];
      [[strongSelf tableView] reloadData];
    }
  }];
}

这是weakSelf/strongSelf 模式的一个相当迂腐的版本,在这种情况下可以做得更简单一些,但它演示了您可能需要的所有部分。您对以下内容进行了弱引用self这样就不会创建保留循环。然后,在完全块中,您采用强引用,以便self这样它就不会在你所在街区的中间消失在你身上。然后你确保self实际上仍然存在,然后才继续。 (自从发消息nil是合法的,你可以跳过strongSelf在这种特殊情况下,步骤是相同的​​。)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当您感兴趣的任务完成时,是什么让完成处理程序执行该块? 的相关文章

随机推荐