0

Ive read quite bit about blocks by now, Apple's Guide, Cocoabuilder, 3 articles on SO and ive used examples in my code that I basically got from online tutorials. Im still trying to understand one specific question. So I decided to make an app with nothing more than a completionHandler example to understand better. This is what I came up with:

ViewController

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

Here is what I understand...

  1. I call fetchUsersWithCompletionHandler from my VC & pass it this completion block. That block takes an NSArray users parameter & returns void.

  2. Meanwhile in the SantiappsHelper Class we have a variable called handler of type ^block which it receives from VC.

  3. The fetchUsersWithCompletionHandler method runs, taking that CompletionBlock parameter, which itself takes the NSArray users parameter? a little confusing.

  4. The webfetch is dispatch_async so it wont block the main thread. So execution on the main thread continues. That new thread executes the fetch synchronously that new thread will stall until the response is returned. Once that new thread receives the response, it fills in the NSHTTPURLResponse. Once it returns, it fills in usersArray with the NSJSONSerialized data.

  5. Finally it reaches the if test and it checks for the existence of the PARAMETER handler that was passed in? A little confusing...the parameter passed in was the completionBlock. Wouldnt that handler parameter always and obviously exist since it was passed in?

  6. Once the handler !NULL then execution is returned to the main thread passing back the NSArray users expected by the block back in the VC.

But if I change the second dispatch to async, the usersArray is properly populated but once handler(usersArray) is sent back to the main thread, its empty or nil! Why?

4

1 回答 1

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

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

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

  4. 正确的

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

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

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

于 2013-09-01T21:04:31.007 回答