美文网首页在项目中踩过的坑
Swift队列遇到的一个栈溢出的问题

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

作者: 子达如何 | 来源:发表于2021-10-18 09:17 被阅读0次

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

    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队列遇到的一个栈溢出的问题

        本文链接:https://www.haomeiwen.com/subject/fzwgoltx.html