2012-03-05 19 views
13

一時停止したタイマーをキャンセルしてから解放しようとしていますが、「dispatch_release」を呼び出すとすぐにEXC_BAD_INSTRUCTIONを取得します。一時停止したタイマーのdispatch_source_cancelが原因でEXC_BAD_INSTRUCTIONが発生する

これは、タイマーで実行する有効な一連のアクションではありませんか?

タイマー作成&サスペンション:

@interface SomeClass: NSObject { } 
@property (nonatomic, assign) dispatch_source_t    timer; 
@end 

// Class implementation 
@implementation SomeClass 

@synthesize timer = _timer; 

- (void)startTimer 
{ 
    dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 
            0, 0, globalQ); 

    dispatch_time_t startWhen = dispatch_walltime(DISPATCH_TIME_NOW, NSEC_PER_SEC * 1); 
    dispatch_source_set_timer(_timer, startWhen, 1 * NSEC_PER_SEC, 5000ull); 

    dispatch_source_set_event_handler(_timer, ^{ 
     // Perform a task 

     // If a particular amount of time has elapsed, kill this timer 
     if (timeConstraintReached) 
     { 
      // Can I suspend this timer within it's own event handler block? 
      dispatch_suspend(_timer); 
     } 
    }); 

    dispatch_resume(_timer); 
} 

- (void)resetTimer 
{ 
    dispatch_suspend(_timer); 

    dispatch_source_cancel(_timer); 

    // dispatch_release causes 
    // 'EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) 
    dispatch_release(_timer); 

    self.timer = nil;  
} 
@end 

はまた、私はタイマーソースのevent_handlerブロック内dispatch_suspend呼び出すことができますか?

ご協力いただければ幸いです。

答えて

24

それがクラッシュした理由は、this codeは次のとおりです。

void 
_dispatch_source_xref_release(dispatch_source_t ds) 
{ 
    if (slowpath(DISPATCH_OBJECT_SUSPENDED(ds))) { 
     // Arguments for and against this assert are within 6705399 
     DISPATCH_CLIENT_CRASH("Release of a suspended object"); 
    } 
    _dispatch_wakeup(ds); 
    _dispatch_release(ds); 
} 

だから、あなたが中断されたdispatch_source_tを解放することはできません。あなたはおそらくそれを中止したくないでしょうresetTimer私は推測します。

私はドキュメントの中で、なぜこのように書かれているのか(なぜなら、私が決して見ることのできないレーダーに賛否両論があることを暗示している)を見つけることができません。 docs where it says

あなたは一時的にdispatch_suspendとdispatch_resumeメソッドを使用して ディスパッチソースのイベントの配信を一時停止し、再開することができます。 これらのメソッドは、 ディスパッチオブジェクトのサスペンド数を増減します。その結果、 dispatch_suspendへの各コールとdispatch_resumeへの一致するコールとのバランスをとって、イベント の配信が再開される前にバランスを取る必要があります。

それはあなたが中断されていますディスパッチソースを解放することはできません言っていない一方で、それはそう、私はそれが下のディスパッチセマフォをその線に沿って何かを使用していますと仮定していますが、それぞれの呼び出しのバランスを取るために持っていると言うんフードはbalanced before they can be releasedでなければなりません。それはちょうど私の推測です:-)。

「timerソースのevent_handlerブロック内でdispatch_suspendを呼び出すことはできますか」については、 dispatch_suspend

のドキュメントのとおり、私はあなたがそうすることができると確信しています。呼び出しが実行されたブロックの完了後に中断が発生します。

+0

ああ、わかりました。すべてのdispatch_suspendは 'オブジェクトのサスペンドカウントをインクリメントする'ため、サスペンドのバランスをとるための 'dispatch_resume'が必要です。 –

+0

event_handlerブロック内でタイマーをキャンセルまたはサスペンドすることは可能ですか?それはブロック外で発生する必要がありますか? –

+0

そうです。私の更新された答えを見てください。ドキュメントが停止しているときには公開できないことを実際には述べていませんが、私はそれが内部的な状態であると考えています。 – mattjgalloway

関連する問題