2016-03-29 2 views
0

Это следующая глава от a question I asked a while back. У меня есть этот упрощенный таймер, он вдохновлен объектом 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 
.... 

Это Безразлично Кажется, это имеет значение, если это структура или класс. То же самое происходит. Я задавался вопросом, нужен ли мне deinit, но я не нашел реализации этого, что заставляет его уйти.

ответ

3

На самом деле причиной сбоя вашего кода является то, что система пытается избавиться от источника события. Вы можете увидеть это, вызвав start() два раза подряд, используя ваш класс. Во время второго вызова вы увидите, что приложение аварийно пытается создать новый источник событий и назначить его в поле source.

Верно также, что если вы не вызываете dispatch_suspend для источника события, авария никогда не произойдет. Вы можете увидеть это в своем примере, если вы закомментируете звонок до stop() перед заменой таймера.

Я не могу объяснить это поведение. Я не знаю, почему вызов dispatch_suspend вызывает сбой при утилизации источника события. Мне кажется, что вы должны сообщать Apple.

Сказав это, в коде не ясно, почему вы бы назвали dispatch_suspend, а не dispatch_source_cancel. Когда вы вызываете stop() на свой таймер, вы делаете это с помощью источника отправки. Если бы вы снова позвонили start(), вы все равно получите новый источник событий. Я рекомендую изменить свою stop() функцию:

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

Это имеет дополнительное преимущество в работе вокруг аварии.

Если вы будете следовать советам, я также рекомендую заменить ваш жестко постоянной с библиотекой отправки символической константы для числа наносекунд в секунду:

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

Я предлагаю это потому, что число нулей легко ошибиться, и использование константы помогает читателю понять, что на самом деле делает код.

Смежные вопросы