Swift 字典:-[__NSCFNumber objectForKey:]:发送到实例的无法识别的选择器

Min*_*ing 5 thread-safety swift

我通过键(对象)访问字典,大多数情况下工作正常,但有时它会崩溃:

-[__NSCFNumber objectForKey:]: 无法识别的选择器发送到实例 0x8000000000000000

我发现的最接近我的问题是这个

这是我的(简化的)代码:

class Meal {
    private static let KEY = Date()
    private static let KEY_Q = DispatchQueue(label: "Meal.KEY")
    static var menus = OrderedDictionary<Date, [Int:Meal]>()

    static func test() throws {
        var date: Date?
        var menu: [Int:Meal]?
        try KEY_Q.sync {
            menu = Meal.menus[KEY] // <-- Error
            if menu == nil {
                date = KEY.clone()
            }
        }
        DispatchQueue.main.async {
            //This needs to run on the UI Thread, since it also loops over Meal.menus
            if date != nil {
                Meal.menus[date!] = [Int:Meal]()
            }
        }
    }
}

class Date: Hashable & Comparable {
    var days = 0
    func hash(into hasher: inout Hasher) {
        hasher.combine(days)
    }

    func clone() -> Date {
        let date = Date()
        date.days = days
        return date
    }
}

class OrderedDictionary<keyType: Hashable, valueType>: Sequence {
    var values = [keyType:valueType]()

    subscript(key: keyType) -> valueType? {
        get {
            return self.values[key]
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 当我的代码在不同的线程中运行时,条目会添加到 中menusUI Thread
  • 存储在字典中的键是KEY不是引用KEY)的克隆
  • 我认为该错误可能是在迁移到 Swift 5 时开始出现的,因此hash(into hasher: inout Hasher)

问题:

  • Swift 字典线程的插入和访问安全吗?
  • 我如何锁定KEY对象和UI Thread
  • 实施是否hash(into hasher: inout Hasher)正确?
  • 为什么会出现此错误以及如何修复它?

Chr*_*ris 6

这看起来非常像一个线程问题(该地址0x8000000000000000非常可疑),因此您可以按照与读取相同的方式序列化写入:

    DispatchQueue.main.async {
        //This needs to run on the UI Thread, since it also loops over Meal.menus
        if let date = date {
            KEY_Q.sync {
                Meal.menus[date] = [Int:Meal]()
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

为了避免在其他地方无意中重新引入错误的可能性,您还可以考虑将队列和访问包装在一个小辅助对象中(这样访问只能发生在正确的队列上)。