我正在开发一个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始终是零
我应该如何设置jsonResponse从urlString返回的JSON?
什么是这个问题的最佳做法是什么?
即在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];
}
有些人可能会说,这是一个可怕的建议,但是你也可以同步下载数据。 它应该在后台排队进行。 它不是最好的做法 ,但对于某些情况(如命令行实用程序,非必要背景队列),它是确定。
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代表团自己排队,确保你不会打电话给你的方法,在同一个队列,因为它会阻止它。
这显然是完成块之前方法将返回。 因为这是该块的主要目的。
你需要改变这样的:
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
}];
}
这是“异步调用”预期的行为。 他们不应阻止调用线程,但执行过块,当调用完成。
只需添加有获得结果的块之后要执行的代码。
因此,而不是...
+ (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:
依赖于呼叫者,简单地把它作为一个参数传递给该方法,并在指定的位置执行它。 如果您需要帮助的是,让我知道。 我会添加它的代码。
另一种解决方案是使用一个信号并等待,直到执行完成处理。 但是,这将阻止用户界面,是不是意图的方式来做到这一点。