2015-07-02 96 views
1

我正在爲我的項目開發一個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

回答

3

塊 - 你的方法不會等待塊完成。

你在這裏

兩種選擇一是一個。發送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

0

很顯然,方法將在塊完成之前返回。因爲這是該塊的主要目的。

你需要改變這樣的:即在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 
    }]; 


} 
1

這是「異步調用」的預期行爲。他們不應該阻止調用線程,而是在調用完成時執行傳遞的塊。

只需在塊中獲得結果後添加必須執行的代碼。

所以不是...

+ (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對象?做什麼的?它總是會是一樣的。 –

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的委託隊列,請確保不要在同一個隊列中調用您的方法,因爲它會阻止它。

+0

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

+0

@SergiiMartynenkoJr對於像你這樣的人我已添加說明。對於像命令行實用程序這樣的特殊情況,這也是一種常見的方法 – Avt

+0

「像我這樣的人」不能默默地按照你的回答沒有評論地移動=) –

相關問題