Swift队列遇到的一个栈溢出的问题

最近项目需要用到链表,随手写了一个简单的实现,未想到遇到了一个奇怪的栈溢出的问题。 先上链表的实现,非常的简单,会有什么问题呢?

open class SwiftDataQueue {
    var identifier: Int
    var data: UnsafeMutableRawPointer
    var next: SwiftDataQueue?

    init(size: Int, identifier id: Int) {
        data = malloc(size)
        identifier = id
        next = nil
    }

    deinit {
        free(data)
    }
}

业务逻辑有很多操作这个队列的地方,偶然的情况下就会出现一个崩溃的情况,崩溃的堆栈是这样的:

#174407 0x00007fff2ff7da30 in _swift_release_dealloc ()
#174408 0x00000001021be677 in outlined destroy of SwiftDataQueue? ()
#174409 0x00000001021be645 in SwiftDataQueue.deinit at SwiftDataQueue.swift:23
#174410 0x00000001021be6a9 in SwiftDataQueue.__deallocating_deinit ()
#174411 0x00007fff2ff7da30 in _swift_release_dealloc ()
#174412 0x00000001021be677 in outlined destroy of SwiftDataQueue? ()
#174413 0x00000001021be645 in SwiftDataQueue.deinit at SwiftDataQueue.swift:23
#174414 0x00000001021be6a9 in SwiftDataQueue.__deallocating_deinit ()
#174415 0x00007fff2ff7da30 in _swift_release_dealloc ()
#174416 0x00000001021be9a1 in static AudioPlayer.stop() at SwiftDataQueue.swift:37

看到这样的堆栈的,我的第一反应是死循环了么?是循环引用导致的么? 在反复检查代码逻辑之后,发现并不存在循环引用,理论上也不应该是循环引用导致死循环,因为,若是循环引用,应该不释放才是。

经过了一阵冷静的思考之后,结合stop时候做的操作是释放链表头,有点怀疑是deinit的递归导致的。

于是,把问题的模型简化为如下逻辑:

public static func test() {
    var head: SwiftDataQueue? = SwiftDataQueue(size: 1024, identifier: 0)
    var current = head
    for i in 1 ..< 102400 {
        let next = SwiftDataQueue(size: 1024, identifier: i)
        current?.next = next
        current = next
    }
    head = nil
}

你看出来是什么问题了吗? 问题就在这里: head = nil 链表的head置为nil时,这个item的引用计数为0,调用deinit,deinit发现next的引用计数也要为0了,于是调用next的deinit,以此类推。。。

解决办法也很简单:

    var current = head?.next
    head = nil
    while current != nil {
        let next = current?.next
        current = next
    }

更近一步的思考:既然这里存在大量的deinit,会不会导致析构耗时太多卡到主线程了呢?如果卡到主线程可以怎么优化呢?

你可能感兴趣的:(Swift队列遇到的一个栈溢出的问题)