2016-03-29 12 views
0

これはa question I asked a while backの次の章です。私はこの単純化されたTimerを持っています。Matt Neuburgの本のTimerオブジェクトに触発されています。この_dispatch_xref_disposeエラーは何を意味しますか?

import Foundation 
import XCPlayground 

class Timer { 
    private var queue = dispatch_queue_create("timer", nil) 
    private var source: dispatch_source_t! 
    var tick:()->() = {} 
    var interval:NSTimeInterval = 1 

    func start() { 
     self.stop() 
     self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.queue) 
     dispatch_source_set_timer(source, DISPATCH_TIME_NOW, UInt64(self.interval) * 1000000000, 0) 
     dispatch_source_set_event_handler(source, self.tick) 
     dispatch_resume(self.source) 
    } 

    func stop() { 
     if self.source != nil { 
      dispatch_suspend(self.source) 
     } 
    } 

} 

var timer = Timer() 
timer.interval = 5 
timer.tick = { print("now \(NSDate())") } 
timer.start() 

timer.stop() 
timer = Timer() // <<--BAD STUFF HAPPENS HERE 

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true 

問題は、私が同じ変数に新しいタイマーを割り当てるポイントです。基本的な使用例は私がタイマーで済んでいるので、私はそれをやめますが、おそらく新しいものを作成して起動したいと思っています。行をマーク遊び場では、

はエラーを与える:

Execution was interrupted, reason: EXC_BAD_INSTRUCTION {code=EXC_I386_INVOP, subcode=0x0). 

私はiOSアプリで同様の何かをする場合、私はのように見える例外トレースを取得:

0: _dispatch_xref_dispose 
5: MyViewController.scanTimer.setter 
.... 

それはdoesnのそれが構造体であれクラスであれ、問題ではないようです。同じことが起こります。私はdeinitが必要かどうか疑問に思っていましたが、それを取り除くための実装が見つかりませんでした。

答えて

3

実際にコードがクラッシュする原因は、システムがイベントソースを破棄しようとしている場合です。クラスを使用してstart()を2回続けて呼び出すと、これを確認できます。 2回目の呼び出しでは、アプリケーションがクラッシュして新しいイベントソースを作成し、sourceフィールドに割り当てようとしていることがわかります。

イベントソースにdispatch_suspendを呼び出さないと、クラッシュが発生しないことも事実です。タイマーを交換する前にstop()へのコールをコメントアウトすると、この例が表示されます。

私はその動作を説明することはできません。なぜ私はdispatch_suspendを呼び出すと、イベントソースを破棄するときにクラッシュするのか分かりません。それはあなたにアップルに報告すべきバグのように私に見えます。

あなたのコードでなぜdispatch_suspendと呼ばれていて、dispatch_source_cancelではないのかは不明です。あなたのタイマーにstop()を呼び出すと、ディスパッチソースが完了します。あなたがstart()に再度電話する場合は、とにかく新しいイベントソースを取得します。これは、クラッシュを回避作業の付加的な利点を持っている

func stop() { 
    if self.source != nil { 
     dispatch_source_cancel(self.source) 
     self.source = nil 
    } 
} 

:私はあなたにstop()機能を変更することをお勧めします。

あなたがアドバイスを取る場合、私はまた、あなたのハードコーディングされた秒でナノ秒の数のディスパッチライブラリーのシンボリック定数と定数の交換をお勧めします:

dispatch_source_set_timer(source, DISPATCH_TIME_NOW, 
    UInt64(self.interval) * NSEC_PER_SEC, 0) 

私はゼロの数ので、このことを示唆間違ってしまうのは簡単で、定数を使うと、コードが実際に何をしているのか理解するのに役立ちます。

関連する問題