2016-11-04 8 views
0

下ProのiPadのアプリをフリーズ:NSRunLoopは、私はこのようになりますカスタム動作の内部同期要求を得た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を追加することで、コード内でそれを修正する1つの方法を見つけ:

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

これはかなりのパフォーマンスを食べると、すべてのNSLogsが除去されるので、長期的に役立つことはありませんリリース構成用。

このバグを修正する方法が必要です。コードが古すぎるかもしれないし、新しい方法があるかもしれませんが、私は分かりません。どんな助けもありがとうございます。

+0

... – Volker

+0

私はrunUntilを試してみました:とRUNMODE:beforeDate:すでに、何も仕事をしたん:(一般に –

+0

、あなたがdispatch_semaphoreまたはディスパッチグループを使用してオフにおそらく優れています –

答えて

0

実行ループからすべてのタイマーなどが削除されるまで、-runメソッドは永遠に終了します。マニュアルでは、

を実行ループからすべての既知の入力ソースとタイマーを手動で削除します。 は、実行ループが終了する保証はありません。 macOSがインストールされ、 は、受信者のスレッドで をターゲットとするリクエストを処理するために、必要に応じて追加の入力ソースを削除します。これらのソースは、実行ループが終了するのを防ぐことができます。

iOS 10は、デバイスがバックグラウンドになっているときにのみ削除される別の入力ソースを追加している可能性があります。または、タイマやソースがない場合は、直前に-runメソッドが常に返されていた可能性があります(ただし、CPUを多く使用してビジー状態になることがあります)。あなたは本当にそのメソッドを使用して多くのコントロールを持っていない、そしておそらく他のメソッドのいくつか。

テストケースコードでは、次のNSRunLoopカテゴリメソッドを使用しましたが、CPUを大量に使用するため、プロダクション用に推奨しているとは思えません.1秒ごとにチェックするように変更することもできます)は、0の間隔である[NSDate date]を使用する代わりに役立ちます。

- (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をチェックして、以前にキャンセルした場合には追加の作業をしないことを確認してください)。

助けになるまで実行するために先に短い時間を与えrunUntil多分
+0

ディスパッチセマフォが動作しています。ありがとう! –

関連する問題