方法在完成处理程序之前返回

问题描述:

我正在为我的项目开发一个networkUtil,我需要一个获取url的方法,并使用NSURLSessionDataTask从该URL返回从该URL获得的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与返回的json从urlString
什么是最佳实践这个问题?

+0

这是因为你在做异步调用。有很多关于它的问题。 – Larme

块 - 你的方法不会等待块完成。

你在这里

两种选择一是一个。发送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]; 
} 
+0

这条线是做什么的? [[NSNotificationCenter defaultCenter] postNotificationName:@“JSONResponse”object:nil userInfo:@ {@“response”:jsonResponse}]; 问题是我想返回jsonResponse,其中一个选项是你的第二个选项,我想知道是否有办法返回jsonResponse。 – Mehdi

+0

此行将通知发布到[NSNotificationCenter](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/)。 你为什么要'返回'它?使用第二种方法来执行您需要的任何操作以获得响应。你问**最佳实践** - 我给你 –

+0

问题是,在另一种方法中,我调用getJsonDataFromURL并将其返回值设置为一个变量,并对其进行一些处理。 '+(NSNumber *)getServerVersion {nkdictionary * versionUpdateJson = [NetworkUtil getJsonDataFromURL:updateServerURL];如果(versionUpdateJson){NSNumber * updateVersion = [NSNumber numberWithFloat:[[versionUpdateJson valueForKey:@“serverVersion”] floatValue]]; return updateVersion; } else { return @ -1; } } '这又一次出现这个问题 – Mehdi

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

你需要改变这样的:即在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:执行的代码依赖于呼叫者,只是把它作为一个参数的方法和执行它在指定的位置。如果您需要帮助,请告诉我。我将为它添加代码。

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

+0

您正在将课程消息发送给对象'self' - 错误。你提出的是违反[关注分离](https://en.wikipedia.org/wiki/Separation_of_concerns)。如果您在程序的不同类中需要使用此方法,并且每个人在获取响应时都做了不同的处理,您会做什么? V –

+0

1.'self'不一定是对实例对象的引用。没有错误。 2.在这种情况下,我会重读我的答案,尤其是:“如果由-getJsonDataFromURL:执行的代码取决于调用者,只需将它作为参数传递给方法并在指定位置执行即可。那就让我知道,我会为它添加代码。“ –

+0

那么,你认为这个方法应该只能被Class对象调用?这个问题的背景含义是错误的。通过你的答案的含义 - 将“执行代码”传递给'self',即Class对象?做什么的?它总是会是一样的。 –

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

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

+0

是的,真的很可怕的建议。完全不是Objective-C的方式。 –

+0

@SergiiMartynenkoJr对于像你这样的人我已添加说明。对于像命令行实用程序这样的特殊情况,这也是一种常见的方法 – Avt

+0

“像我这样的人”不能默默地按照你的回答没有评论地移动=) –