completionhandler 之前的方法 returns
method returns before completionhandler
我正在为我的项目开发一个 networkUtil,我需要一个方法来获取 url 和 returns 从 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 和 方法本身 在 上 运行不同的线程,在最后一行中,json响应总是nil
我应该如何设置 jsonResponse 从 urlString 返回 json?
这个问题的最佳实践是什么?
很明显Method会在Block完成之前return。因为这是该区块的主要目的。
您需要这样更改:
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
}];
}
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];
}
这是 "asynchronous calls" 的预期行为。它们不应阻塞调用线程,而是在调用完成后执行传递的块。
只需在块中添加获取结果后必须执行的代码即可。
所以不是……
+ (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:
执行的代码依赖于调用者,只需将其作为参数传递给方法并在指定位置执行即可。如果您为此需要帮助,请告诉我。我会为它添加代码。
另一个解决方案是使用信号量并等待,直到完成处理程序被执行。但这会阻止 UI 并且不是预期的方式。
有些人可能会说这是一个可怕的建议,但您也可以同步下载数据。它应该在后台队列中完成。这不是最佳实践,但在某些情况下(例如命令行实用程序、非关键后台队列)没问题。
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 属性 用于 "delegate method calls and completion handlers related to the session"。默认情况下,NSURLSession 总是在初始化期间创建一个新的 delegateQueue。但是,如果您自己设置 NSURLSession 的委托队列,请确保您不会在同一队列中调用您的方法,因为它会阻止它。
我正在为我的项目开发一个 networkUtil,我需要一个方法来获取 url 和 returns 从 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 和 方法本身 在 上 运行不同的线程,在最后一行中,json响应总是nil
我应该如何设置 jsonResponse 从 urlString 返回 json?
这个问题的最佳实践是什么?
很明显Method会在Block完成之前return。因为这是该区块的主要目的。
您需要这样更改:
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
}];
}
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];
}
这是 "asynchronous calls" 的预期行为。它们不应阻塞调用线程,而是在调用完成后执行传递的块。
只需在块中添加获取结果后必须执行的代码即可。
所以不是……
+ (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:
执行的代码依赖于调用者,只需将其作为参数传递给方法并在指定位置执行即可。如果您为此需要帮助,请告诉我。我会为它添加代码。
另一个解决方案是使用信号量并等待,直到完成处理程序被执行。但这会阻止 UI 并且不是预期的方式。
有些人可能会说这是一个可怕的建议,但您也可以同步下载数据。它应该在后台队列中完成。这不是最佳实践,但在某些情况下(例如命令行实用程序、非关键后台队列)没问题。
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 属性 用于 "delegate method calls and completion handlers related to the session"。默认情况下,NSURLSession 总是在初始化期间创建一个新的 delegateQueue。但是,如果您自己设置 NSURLSession 的委托队列,请确保您不会在同一队列中调用您的方法,因为它会阻止它。