2016-11-04 70 views
0

下,我得到了一個自定義操作中同步請求,看起來像這樣:NSRunLoop凍結在iPad上的應用程序臨iOS10

NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; 
NSURLSession* session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil]; 
NSURLSessionDataTask* task = [session dataTaskWithRequest:request completionHandler:^(NSData *_data, NSURLResponse *_response, NSError *_error) 
           { 
            self->data = [_data retain]; 
            self->tempResponse = _response; 
            self->tempError = [_error retain]; 
            self->done = true; 
           }]; 

[task resume]; 

// wait until the connection has finished downloading the data or the operation gets cancelled 
while (!self->done && !self.isCancelled) 
{ 
    [[NSRunLoop currentRunLoop] run]; 
} 

此代碼是很老,但已經經歷了多個版本的IOS工作(只更換NSURLConnection的一些點)。現在在iPad Pro上的iOS 10下,此代碼將凍結我的應用程序。如果我將應用程序置於後臺並重新打開它,它將再次運行。另外,如果我在[task resume]self->data = [_data retain];上放置斷點,則根本不會發生凍結。

我發現修復它裏面的代碼的一種方式,通過添加NSLog的運行循環:

while (!self->done && !self.isCancelled) 
{ 
    NSLog(@"BLAH!"); 
    [[NSRunLoop currentRunLoop] run]; 
} 

這吃頗有些性能,從長遠來看無助,因爲所有NSLogs被刪除用於發佈配置。

所以我需要一種方法來解決這個錯誤。也許代碼太老了,有一種新的方式來做到這一點,我不知道。任何幫助表示讚賞。

+0

也許runUntil給予短時間提前運行,直到幫助... – Volker

+0

我試過runUntil:和runMode:beforeDate:已經沒有什麼工作:( –

+0

一般來說,你可能會更好使用dispatch_semaphore或調度組 –

回答

0

-run方法將永遠持續下去,直到從運行循環中刪除所有定時器等。該文件指出:

手動從運行循環 刪除所有已知的輸入源和定時器是不能保證運行退出循環。 macOS可以安裝並根據需要移除其他輸入源,以便在接收方的線程處理目標爲 的請求。這些來源因此可以防止運行循環退出。

所以,這是可能的的iOS 10被加入當該裝置中背景,或類似的東西,其僅取出另一輸入源。或者,如果沒有定時器或來源(但是這將是一個忙於使用大量CPU的等待),那麼-run方法可能總是立即返回。你真的沒有太多的控制使用這種方法,可能還有其他的一些。我在測試用例代碼中使用了下面的NSRunLoop類別方法,但我不確定我會推薦它用於生產用途,因爲它會大量使用CPU,雖然也許更改爲檢查每個.1秒(或更長) )會有所幫助,而不是使用[NSDate date],這是一個0間隔。

- (BOOL)runWithTimeout:(NSTimeInterval)timeout untilCondition:(BOOL (^)(void))testBlock 
{ 
    NSDate *limitDate = [NSDate dateWithTimeIntervalSinceNow:timeout]; 

    while (1) 
    { 
     if (testBlock()) 
      return YES; 
     if ([NSDate timeIntervalSinceReferenceDate] > [limitDate timeIntervalSinceReferenceDate]) 
      return NO; 
     if (![self runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]]) 
      return NO; 
    } 
} 

的更常見的方式做NSURLSession同步調用(在一個子線程,我假設)是使用旗語:

dispatch_semaphore_t sem = dispatch_semaphore_create(0); 
NSURLSessionDataTask* task = [session dataTaskWithRequest:request completionHandler:^(NSData *_data, NSURLResponse *_response, NSError *_error) 
          { 
           self->data = [_data retain]; 
           self->tempResponse = _response; 
           self->tempError = [_error retain]; 
           dispatch_semaphore_signal(sem); 
          }]; 

[task resume]; 
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); 

這可以根據需要進行復雜的同時檢查取消 - 但你可以使信號量成爲實例變量,並覆蓋-cancel方法來調用信號量。確保不要再使用該信號量實例,因爲您可能有多個信號(並且您可能希望在網絡完成塊中檢查self.isCancelled,以確保您以前取消時不會執行任何其他工作)。

+0

調度信號正在工作,謝謝! –