ios - 访问swift数组时swift_isUniquelyReferenced_nonNull_native中的EXC_BAD_ACCESS

标签 ios swift xcode memory-management exc-bad-access

问题

在编写单元测试并 mock 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

只需执行单元测试并看到它崩溃。

问题

这里发生了什么?我观察到内部崩溃 swift_isUniquelyReferenced_nonNull_native已经在其他项目中了,我很想完全理解这次失败的原因!那么找出问题的过程是怎样的呢?以及如何修复它?

独立复制项目

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

最佳答案

崩溃是由于未初始化的成员(它是 NSObject 不是常规的 swift 类,因此需要显式 init(),但由于这是 Timer,它具有半抽象的指定初始化程序,因此不允许覆盖)。

解决方案是明确设置错过的 ivar,如下所示。

使用 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
}

关于ios - 访问swift数组时swift_isUniquelyReferenced_nonNull_native中的EXC_BAD_ACCESS,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61006622/

相关文章:

ios - 核心数据迁移: Could not cast value of type 'NSManagedObject_MyType' to 'MyModule.MyType'

iphone - 静音内置 iPhone 麦克风

ios - Swift到Swift 2

xcode - 跳转到定义转到静态库头文件的派生数据文件夹,而不是静态库项目

ios - 如何在 Interface Builder (Xcode 9) 中工作的 UIButton 子类中设置默认标题

swift - 如何将 SCNRenderer 与现有的 MTLCommandBuffer 结合使用?

ios - 无法使用 Swift 读取 Firebase 数据

xcode - 操作系统 X : Swift : Open Safari (or default browser) with POST

ios - 如何访问存储在 Root.plist 设置包中的信息 (Swift)

ios - "Unexpectedly found nil while unwrapping an Optional value"添加导航时