美文网首页
Swift中as? 引发的线程问题

Swift中as? 引发的线程问题

作者: lucas_ljj | 来源:发表于2020-12-02 14:01 被阅读0次

    最近在排查fireBase上 线上的崩溃时,发现了一个很有趣的as?引发的野指针的问题。

    先上代码,主要是对YYMemoryCache 读写的一个封装

        internal func set(_ data: SparkChartData) {
    
            
            let cacheData = Data(
                timestamp: Date().timeIntervalSince1970,
                data: data
            )
            self.cache.setObject(cacheData, forKey: NSString(string: "\(data.tickerId)"))
        }
    
        internal func get(_ tickerId: Int64) -> SparkChartData? {
            guard let cacheData = self.cache.object(forKey: NSString(string: "\(tickerId)")) as? SparkChartDataCache.Data else {
                return nil
            }
            return cacheData.data
        }
    

    其中cache是YYMemoryCache。我们知道YYMemoryCache的所有方法包括setObject getobject都是用pthread_mutex_lock锁住线程安全的。所以即使这2个方法是高频多线程调用的我们在review代码时也没有发现问题。

    但是线上的fireBase记录却实实在在的告诉我们这段代码是有野指针的。虽然概率极低。不过我们在打印的堆栈信息中发现了蛛丝马迹

    0
    libswiftCore.dylib
    swift_getObjectType + 48
    1
    libswiftCore.dylib
    _bridgeAnyObjectToAny(_:) + 32
    2
    **********
    <compiler-generated> 
    SparkChartDataCache.get(_:) + 4307961656
    

    _bridgeAnyObjectToAny 和 swift_getObjectType 是 as? 的内部实现。在swift中的类型转换和object-C中的类型转换实现是完全不同的。

    self.cache.object(forKey: NSString(string: "\(tickerId)")) as? SparkChartDataCache.Data
    

    虽然这个代码看起来只有一行。但是实际执行的时候 其实会被分解成2步来完成。
    1.self.cache.object(forKey: NSString(string: "(tickerId)"))

    1. as ? SparkChartDataCache.Data

    步骤1 是在YYMemoryCache 保障的线程安全。而步骤二其实已经离开pthread_mutex_lock的作用范围。也就是说在1,2步骤之间正好有别的线程执行了set方法,而YYMemoryCache的set的实现我们可以发现,是先delete再set的。从而导致了步骤1得到的指针在步骤2时变成了一个野指针。虽然概率极低,但是高并发时还是会出现的。

    最后我们在get和set用信号量做了读写锁之后,崩溃消失。虽然我们的锁其实会覆盖YYMemoryCache内部的锁,但是对效率影响不大。

    相关文章

      网友评论

          本文标题:Swift中as? 引发的线程问题

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