EXC_BAD_ACCESS в swift_isUniquelyReferenced_nonNull_native при доступе к массиву swift - PullRequest
5 голосов
/ 03 апреля 2020

Проблема

При написании юнит-тестов и макете NSTimer Я вижу

Exception: EXC_BAD_ACCESS (code=1, address=0x8)

внутри

swift_isUniquelyReferenced_nonNull_native

Ситуация возникает при доступе массив invalidateInvocations (внутри func invalidate()) здесь.

class TimerMock: Timer {

    /// Timer callback type
    typealias TimerCallback = ((Timer) -> Void)

    /// The latest used timer mock accessible to control
    static var  currentTimer: TimerMock!

    /// The block to be invoked on a firing
    private var block:        TimerCallback!

    /// Invalidation invocations (will contain the fireInvocation indices)
    var invalidateInvocations: [Int] = []

    /// Fire invocation count
    var fireInvocations:       Int   = 0

    /// Main function to control a timer fire
    override open func fire() {
        block(self)
        fireInvocations += 1
    }

    /// Hook into invalidation
    override open func invalidate() {
        invalidateInvocations.append(fireInvocations)
    }

    /// Hook into the timer configuration
    override open class func scheduledTimer(withTimeInterval interval: TimeInterval,
                                            repeats: Bool,
                                            block: @escaping TimerCallback) -> Timer {
        // return timer mock
        TimerMock.currentTimer = TimerMock()
        TimerMock.currentTimer.block = block
        return TimerMock.currentTimer
    }

}

Интересно, что если я поменяю invalidateInvocations на обычный Int, он будет доступен без сбоев.

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

Демонстрация

Вы можно увидеть полный пример запуска и сбоя в этом репозитории (ветвь demo/crash)

https://github.com/nomad5modules/ArcProgressViewIOS/tree/demo/crash

Просто выполните модульные тесты и увидите, что они дают сбой.

Вопрос

Что здесь происходит? Я наблюдал cra sh внутри swift_isUniquelyReferenced_nonNull_native уже в других проектах, и я хотел бы полностью понять причину этой неудачи! Так как происходит процесс, чтобы выяснить, что здесь не так? И как это исправить?

Автономный проект воспроизведения

https://drive.google.com/file/d/1fMGhgpmBRG6hzpaiTM9lO_zCZwNhwIpx/view?usp=sharing

1 Ответ

2 голосов
/ 09 апреля 2020

Cra sh происходит из-за неинициализированного члена (это NSObject, а не обычный swift-класс, поэтому потребуется явный init (), но, поскольку это Timer, он имеет полу-абстрактный обозначенный инициализатор, поэтому переопределение не допускается ).

Решение состоит в том, чтобы явно настроить пропущенный ивар, как показано ниже.

Протестировано и работает над вашим тестовым проектом с Xcode 11.4.

override open class func scheduledTimer(withTimeInterval interval: TimeInterval,
                                        repeats: Bool,
                                        block: @escaping TimerCallback) -> Timer {
    // return timer mock
    TimerMock.currentTimer = TimerMock()
    TimerMock.currentTimer.invalidateInvocations = [Int]()   // << fix !!
    TimerMock.currentTimer.block = block
    return TimerMock.currentTimer
}
...