1

我正在为我的项目开发一个 networkUtil,我需要一个获取 url 并返回从该 url 接收到的 JSON 的方法,使用 NSURLSessionDataTask 从服务器获取 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 始终为nil

我应该如何使用从urlString返回的 json设置jsonResponse? 这个问题的最佳实践是什么?

4

4 回答 4

3

在 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];
}
于 2015-07-02T08:08:38.440 回答
1

有些人可能会说这是一个糟糕的建议,但您也可以同步下载数据。它应该在后台队列中完成。这不是最佳实践,但在某些情况下(如命令行实用程序、非关键后台队列)是可以的。

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 的委托队列,请确保你不要在同一个队列中调用你的方法,因为它会阻塞它。

于 2015-07-02T08:30:14.283 回答
0

显然 Method 会在 Block 完成之前返回。因为这是该块的主要目的。

您需要更改如下内容:

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
    }];


}
于 2015-07-02T08:03:11.853 回答
0

这是“异步调用”的预期行为。当调用完成时,它们不应阻塞调用线程,而是执行传递的块。

只需添加在块中获得结果后必须执行的代码。

所以不是……</p>

+ (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:依赖于调用者,只需将其作为参数传递给方法并在指定位置执行即可。如果您需要帮助,请告诉我。我将为其添加代码。

另一种解决方案是使用信号量并等待,直到执行完成处理程序。但这会阻止用户界面,并且是非预期的方式。

于 2015-07-02T08:15:29.337 回答