2014-06-14 51 views
0

我想獲得以下內容:我在NSOperationQueue中有兩個NSOperations。首先是從網站下載(獲取一些json數據),下一個是解析該數據。這是依賴性操作。 我不明白如何將它們連接在一起。如果它們都被分配並且在隊列中,那麼如何將json字符串轉換爲解析它的操作?如果這個隊列在另一個NSOperationQueue內執行一個由前面提到的兩個NSOperation組成的NSOperation,那麼這是一個問題嗎?NSOperation之間的數據傳輸

我能找到的所有數據都是傳遞給主線程(performSelectorOnMainThread)上的委託,但我需要在後臺執行所有這些操作。

謝謝。 代碼: NSDownload:的NSOperation

- (instancetype)initWithURLString:(NSString *)urlString andDelegate:(id<JSONDataDelegate>)delegate 
{ 
    self = [super init]; 
    if (self) { 
     _urlStr = urlString; 
     _delegate = delegate; /// this needs to be a NSOPeration 
     _receivedData = [NSMutableData dataWithCapacity:256]; 
    } 
    return self; 
} 

#pragma mark - OVERRIDE 

    - (void)main 
    { 
     @autoreleasepool { 

      if (self.isCancelled) { 
       return; 
      } 

      NSURL *url = [NSURL URLWithString:self.urlStr]; 
      NSURLRequest *request = [NSURLRequest requestWithURL:url]; 
      self.urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]; 
     } 
    } 

    #pragma mark - NSURLConnectionDataDelegate 

    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
    { 
     if (self.isCancelled) { 
      [connection cancel]; 
      self.receivedData = nil; 
      return; 
     } 
     [self.receivedData appendData:data]; 
    } 

    - (void)connectionDidFinishLoading:(NSURLConnection *)connection 
    { 
     if (self.isCancelled) { 
      self.receivedData = nil; 
      return; 
     } 
     // return data to the delegate 
     NSDictionary *responseDict = @{JSON_REQUESTED_URL : self.urlStr, 
             JSON_RECEIVED_RESPONSE : self.receivedData}; 
     [(NSObject *)self.delegate performSelectorOnMainThread:@selector(didReceiveJSONResponse:) withObject:responseDict waitUntilDone:NO]; // ok to uses performSelector as this data is not for use on the main thread ??? 
    } 

    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 
    { 
     // return error to the delegate 
     [(NSObject *)self.delegate performSelectorOnMainThread:@selector(didFailToReceiveDataWithError:) withObject:error waitUntilDone:NO]; 
    } 
+0

你真的需要同時創建兩個'NSOperations'嗎?你能不能創建'DownloadOperation'並將其添加到隊列中,並在完成後創建'ParseOperation'並將其添加到隊列中? – lucianomarisi

+0

這將是可能的。但是,NSOperations之間的依賴關係是什麼?他們不應該是一個管道? – user1028028

+0

您的應用程序的體系結構真的取決於您,但您可以讓'DownloadOperation'將數據保存在局部變量中(在「NSOperations」的範圍內並將「ParseOperation」添加爲將使用此變量的依賴項。或者在'DownloadOperation'的完成塊中,您可以將下載的數據設置到'ParseOperation',所以當'ParseOperation'開始時數據就在那裏。也許發佈當前的代碼來討論什麼是最合適的解決方案。 – lucianomarisi

回答

1

@ user1028028:

用下面的辦法。

1)維護您用於添加DownloadOperation的操作隊列引用。

2)在connectionDidFinishLoading方法中,創建ParseOperation實例,設置json數據並將其添加到操作隊列中。在DownloadOperation中維護ParseOperation強引用變量,並通過DownloadOperation接口處理取消解析操作。

3)完成解析後,調用主線程中的UI功能。

我希望這會有所幫助。

+0

這是w我現在也在使用的帽子。然而,我使用performSelector,而不是performSelectorOnMain線程..它的工作原理...應該不會崩潰? – user1028028

+0

如果您遵循上述方法,您是否按預期獲得結果? – PurnachandraObulasetty

+0

是的,結果如預期...不知道執行選擇器發生什麼線程,但嘿它工作 – user1028028

1

由於lucianomarisi筆記,這通常會是最好的只是第一操作產生第二操作。這通常更容易管理。操作依賴關係在我的經驗中並不常見。

也就是說,它當然可以在操作之間傳遞數據。例如,您可以在第二個操作中創建一個datasource屬性。那將是要求其數據的對象;該對象將是第一個操作。不過,這種方法可能需要鎖定。

您還可以在第一個操作中創建一個nextOp屬性。完成後,它將在退出之前在第二個操作中調用setData:。你可能不需要爲此鎖定,但你可能會。在大多數情況下,對於第一次手術來說,在這一點上安排nextOp會更好(這又看起來像lucianomarisi的答案)。

問題是操作只是一個對象。它可以有任何你想要的方法和屬性。你可以將一個操作傳遞給另一個操作。

請記住,由於操作在後臺運行,因此沒有理由需要使用到NSURLConnection的異步接口。同步API(sendSynchronousRequest:returningResponse:error:是罰款,這一點,和更簡單的代碼,你甚至可以用一個簡單的NSBlockOperation。或者,你可以使用異步NSURLConnection接口,但你真的不需要一個NSOperation

我還注意到:

_receivedData = [NSMutableData dataWithCapacity:256]; 

難道真的這麼小的一塊JSON數據,這是很難相信,這種複雜性是值得的移動如此小的解析操作的背景

(作爲一個方面?注意,除非你確切知道內存的大小,否則通常手動指定容量通常不會有多大好處。即使如此,它並不總是清楚這是一種好處。我相信NSURLConnection現在正在使用下面的調度數據,所以你實際上要求一個永遠不會使用的內存塊。當然可可也不會分配它,因爲它優化了這一點......重點在於你可能只是使用[NSMutableData data]。可可對這些事情很聰明,你通常只能得到它的優化方式。)

+0

請參閱關於爲什麼我使用異步請求而不是同步請求的信息,請參見其他答案 – user1028028

0

正如Rob所說,除非你有任何特別的理由使用操作,否則使用synchronized調用。然後在MainThread或您需要的任何其他線程上執行選擇器。除非你想在單獨的操作或線程(明確)中分開檢索和解析。

這裏是我使用JSON檢索和解析的代碼:

-(BOOL) loadWithURL:(NSString*) url params: (NSDictionary*) params andOutElements:(NSDictionary*) jElements 

{

NSError *reqError = nil; 
NSString* urlStr = @"";//@"http://"; 
urlStr = [urlStr stringByAppendingString:url]; 

NSURL* nsURL = [NSURL URLWithString:urlStr]; 

//Private API to bypass certificate ERROR Use only for DEBUG 
//[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[nsURL host]]; 


NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: nsURL 
                 cachePolicy:NSURLRequestUseProtocolCachePolicy 
                timeoutInterval:60.0]; 

[request setHTTPMethod:@"POST"]; 
NSString *postString = @""; 
if(params!=nil) { 
    NSEnumerator* enumerator = params.keyEnumerator; 
    NSString* aKey = nil; 
    while ((aKey = [enumerator nextObject]) != nil) { 
     NSString* value = [params objectForKey:aKey]; 

     //Use our own encoded implementation instead of above Apple one due to failing to encode '&' 
     NSString* escapedUrlString =[value stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]; 
     //Required to Fix Apple bug with not encoding the '&' to %26 
     escapedUrlString = [escapedUrlString stringByReplacingOccurrencesOfString: @"&" withString:@"%26"]; 

     //this is custom append method. Please implement it for you -> the result should be 'key=value' or '&keyNotFirst=value' 
     postString = [self appendCGIPairs:postString key:aKey value:escapedUrlString isFirst:false]; 
    } 
} 


//************** Use custom enconding instead !!!! Error !!!!! ************** 
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]]; 


NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&reqError]; 
if(reqError!=nil) { 
    NSLog(@"SP Error %@", reqError); 
    return NO; 
} 

NSString *json_string = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding]; 
//Handles Server Errors during execution of the web service that handles the call. 
if([json_string hasPrefix:@"ERROR"] == YES){ 
    NSLog(@"SP Error %@", lastError); 
    return NO; 
} 

//Very Careful!!!!!! Will stop for any reason.!!!!!! 
//Handles errors from IIS Server that serves teh request. 
NSRange range = [json_string rangeOfString:@"Runtime Error"]; 
if(range.location != NSNotFound) { 
    NSLog(@"SP Error %@", lastError); 
    return NO; 
} 

//Do the parsing 
jElements = [[parser objectWithString:json_string error:nil] copy]; 
if([parser error] == nil) { 
    NSLog(@"Parsing completed"); 
} else { 
    jElements = nil; 
    NSLog(@"Json Parser error: %@", parser.error); 
    NSLog(@"Json string: %@", json_string); 
    return NO; 
} 

//Parsed JSON will be on jElements 
return YES; 

}

+0

我使用異步NSURLConnecton的原因是能夠儘快取消操作的加載II要使用同步請求我將無法取消它,直到json加載後(可能超過1 MB) – user1028028