Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EXC_BAD_ACCESS in swift_isUniquelyReferenced_nonNull_native when accessing swift array

The Problem

While writing unit tests and mocking away the NSTimer I am seeing an

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

inside

swift_isUniquelyReferenced_nonNull_native

The situation appear when accessing the array invalidateInvocations (inside func invalidate()) here.

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
    }

}

The interesting thing is if I change invalidateInvocations to a regular Int it can be accessed without any crashes.

Because accessing this variable leads to a EXC_BAD_ACCESS I would assume the array was already deallocated, but I would not see how this could happen.

The Demo

You can see a full running and crashing example in this repository (branch demo/crash)

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

Simply execute the unit tests and see it crashing.

The Question

What is happening here? I have observed a crash inside swift_isUniquelyReferenced_nonNull_native already in other projects too and I would love to completely understand the reason for this failure! So how is the process to find out what wrong here? And how to fix it?

Standalone reproduction project

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

like image 806
Martin Mlostek Avatar asked Apr 03 '20 07:04

Martin Mlostek


1 Answers

The crash is due to not initialised member (it is NSObject not regular swift class, so explicit init() would be needed, but as this is Timer it has semi-abstract designated initializer, so override is not allowed).

The solution is to set up missed ivar explicitly, as below.

Tested & works on your Test project with 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
}
like image 126
Asperi Avatar answered Nov 11 '22 23:11

Asperi