方法completionhandler前返回(method returns before compl

2019-10-23 14:44发布

我正在开发一个networkUtil我的项目,我需要得到一个URL并返回使用NSURLSessionDataTask从服务器获取一个JSON从网址获得的JSON的方法。 该方法如下:

+ (NSDictionary*) getJsonDataFromURL:(NSString *)urlString{
    __block NSDictionary* jsonResponse;
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:urlString] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
        NSLog(@"%@", jsonResponse);
    }];

    [dataTask resume];

    return jsonResponse;
}

问题是,我的方法和方法本身内部的completionHandler不同的线程运行,并在最后一行jsonResponse始终是

我应该如何设置jsonResponseurlString返回的JSON?
什么是这个问题的最佳做法是什么?

Answer 1:

即在NSURLSession运行块在不同的线程上运行 - 你的方法不会等待块完成。

你有两个选择这里

第一个 。 发送NSNotification

+ (void) getJsonDataFromURL:(NSString *)urlString{
       NSURLSession *session = [NSURLSession sharedSession];
       NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:urlString] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
           NSDictionary* jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
           NSLog(@"%@", jsonResponse);

           [[NSNotificationCenter defaultCenter] postNotificationName:@"JSONResponse" object:nil userInfo:@{@"response" : jsonResponse}];
       }];

       [dataTask resume];
}

第二个 。 过去完成块以该实用程序的方法

+ (void) getJsonDataFromURL:(NSString *)urlString
            completionBlock:(void(^)(NSDictionary* response))completion {
       NSURLSession *session = [NSURLSession sharedSession];
       NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:urlString] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
           NSDictionary* jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
           NSLog(@"%@", jsonResponse);

           completion(jsonResponse);
       }];

       [dataTask resume];
}


Answer 2:

有些人可能会说,这是一个可怕的建议,但是你也可以同步下载数据。 它应该在后台排队进行。 它不是最好的做法 ,但对于某些情况(如命令行实用程序,非必要背景队列),它是确定。

NSURLSession没有同步的下载方法,但你可以很容易地与信号绕过它:

+ (NSDictionary*) getJsonDataFromURL:(NSString *)urlString{
    __block NSDictionary* jsonResponse;

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); // Line 1

    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:urlString] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
        NSLog(@"%@", jsonResponse);

        dispatch_semaphore_signal(semaphore); // Line 2
    }];

    [dataTask resume];

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // Line 3

    return jsonResponse;
}

NSURLSession有delegateQueue它用于“委托方法调用和完成处理有关会话”属性。 默认情况下,NSURLSession总是创建初始化过程中一个新的delegateQueue。 但是,如果你设置了NSURLSession代表团自己排队,确保你不会打电话给你的方法,在同一个队列,因为它会阻止它。



Answer 3:

这显然是完成块之前方法将返回。 因为这是该块的主要目的。

你需要改变这样的:

NSDictionary* jsonResponse;

+ (NSDictionary*) getJsonDataFromURL:(NSString *)urlString{

    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:urlString] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        self.jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
        NSLog(@"%@", jsonResponse);

        [dataTask resume];

// ad observer here that call method to update your UI
    }];


}


Answer 4:

这是“异步调用”预期的行为。 他们不应阻止调用线程,但执行过块,当调用完成。

只需添加有获得结果的块之后要执行的代码。

因此,而不是...

+ (NSDictionary*) getJsonDataFromURL:(NSString *)urlString
{
  __block NSDictionary* jsonResponse;
  NSURLSession *session = [NSURLSession sharedSession];
  NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:urlString] completionHandler:
  ^(NSData *data, NSURLResponse *response, NSError *error) 
  {
    jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
    NSLog(@"%@", jsonResponse);
  }];

  [dataTask resume];

  return jsonResponse;
}
…
NSDictionary* jsonResponde = [self getJsonDataFromURL:url]; // BTW: get infringes the naming rules of Objective-C
// The code that has to be executed is here

… 你做这个:

+ (void) getJsonDataFromURL:(NSString *)urlString
{
  NSURLSession *session = [NSURLSession sharedSession];
  NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:urlString] completionHandler:
  ^(NSData *data, NSURLResponse *response, NSError *error) 
  {
    NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
    NSLog(@"%@", jsonResponse);
    // Place the code to be executed here <-----
  }];

  [dataTask resume];
}
…
[self getJsonDataFromURL:url]; // BTW: get infringes the naming rules of Objective-C
// No code here any more

如果由执行的代码-getJsonDataFromURL:依赖于呼叫者,简单地把它作为一个参数传递给该方法,并在指定的位置执行它。 如果您需要帮助的是,让我知道。 我会添加它的代码。

另一种解决方案是使用一个信号并等待,直到执行完成处理。 但是,这将阻止用户界面,是不是意图的方式来做到这一点。



文章来源: method returns before completionhandler