【问题标题】:Xcode 9 and Xcode 10 giving different results, even with same swift versionXcode 9 和 Xcode 10 给出不同的结果,即使使用相同的 swift 版本
【发布时间】:2018-12-14 18:13:23
【问题描述】:

我在 xcode 9.3 和 xcode 10 beta 3 Playground 中运行此代码

import Foundation

public protocol EnumCollection: Hashable {
    static func cases() -> AnySequence<Self>
}

public extension EnumCollection {

    public static func cases() -> AnySequence<Self> {
        return AnySequence { () -> AnyIterator<Self> in
            var raw = 0
            return AnyIterator {
                let current: Self = withUnsafePointer(to: &raw) { $0.withMemoryRebound(to: self, capacity: 1) { $0.pointee } }

                guard current.hashValue == raw else {
                    return nil
                }

                raw += 1
                return current
            }
        }
    }
}

enum NumberEnum: EnumCollection{
    case one, two, three, four
}

Array(NumberEnum.cases()).count

尽管两者都使用 swift 4.1,但它们给了我不同的结果

关于 xcode 9.3 数组的大小为 4

xcode 10 beta 3 上 数组的大小为 0

我完全不明白。

【问题讨论】:

    标签: swift xcode xcode10


    【解决方案1】:

    这是一种获取所有枚举值序列的未记录方式, 并且只是偶然地与早期的 Swift 版本一起工作。它依赖于 枚举值的哈希值是连续整数, 从零开始。

    这绝对不再适用于 Swift 4.2(即使正在运行 在 Swift 4 兼容模式下)因为哈希值现在总是 随机,见SE-0206 Hashable Enhancements

    为了降低哈希值的可预测性,标准哈希函数默认使用每次执行的随机种子。

    你可以用

    来验证
    print(NumberEnum.one.hashValue)
    print(NumberEnum.two.hashValue)
    

    使用 Xcode 10 打印 01,但有些 其他值也随每个程序运行而变化。

    有关正确的 Swift 4.2/Xcode 10 解决方案,请参阅How to enumerate an enum with String type?

    extension NumberEnum: CaseIterable  { }
    print(Array(NumberEnum.allCases).count) // 4
    

    【讨论】:

    • 感谢您的更正。只是为了我的理解,所以逻辑哈希函数很快对吗?它如何随 xcode 版本变化?
    • @kr15hna:它是 Swift 标准库或 Swift 运行时的一部分,并且都带有 Xcode 应用程序。
    • 所以即使我在 xcode 10 中运行 4.1 也无法正常工作,所以这个 4.1 与 xcode 9 4.1 不同?
    • @kr15hna:没有。Xcode 10 带有 Swift 4.2,它具有适用于 Swift 3 和 4 的兼容模式
    【解决方案2】:

    Xcode 10 和 Swift 4.2 及更高版本的解决方案如下。

    第 1 步: 创建协议 EnumIterable。

    protocol EnumIterable: RawRepresentable, CaseIterable {
        var indexValue: Int { get }
    }
    
    extension EnumIterable where Self.RawValue: Equatable {
        var indexValue: Int {
            var index = -1
            let cases = Self.allCases as? [Self] ?? []
            for (caseIndex, caseItem) in cases.enumerated() {
                if caseItem.rawValue == self.rawValue {
                    index = caseIndex
                    break
                }
            }
            return index
        }
    }
    

    第 2 步:将 EnumIterator 协议扩展到您的枚举。

    enum Colors: String, EnumIterable {
        case red = "Red"
        case yellow = "Yellow"
        case blue = "Blue"
        case green = "Green"
    }
    

    第 3 步: 像使用 hashValue 一样使用 indexValue 属性。

    Colors.red.indexValue
    Colors.yellow.indexValue
    Colors.blue.indexValue
    Colors.green.indexValue
    

    示例打印语句和输出

    print("Index Value: \(Colors.red.indexValue), Raw Value: \(Colors.red.rawValue), Hash Value: \(Colors.red.hashValue)")
    

    输出:“索引值:0,原始值:红色,哈希值:1593214705812839748”

    print("Index Value: \(Colors.yellow.indexValue), Raw Value: \(Colors.yellow.rawValue), Hash Value: \(Colors.yellow.hashValue)")
    

    输出:“索引值:1,原始值:黄色,哈希值:-6836447220368660818”

    print("Index Value: \(Colors.blue.indexValue), Raw Value: \(Colors.blue.rawValue), Hash Value: \(Colors.blue.hashValue)")
    

    输出:“索引值:2,原始值:蓝色,哈希值:-8548080225654293616”

    print("Index Value: \(Colors.green.indexValue), Raw Value: \(Colors.green.rawValue), Hash Value: \(Colors.green.hashValue)") 
    

    输出:“索引值:3,原始值:绿色,哈希值:6055121617320138804”

    【讨论】:

      【解决方案3】:

      如果您使用枚举的 hashValue 来确定 case 值(位置或 id),这是一种错误的方法,因为它不能保证返回连续的 int 值,0,1,2... 它不再适用于迅捷4.2

      例如,如果您使用这样的枚举:

      enum AlertResultType {
          case ok, cancel 
      }
      

      这个枚举的 hashValue 可能会返回一个大的 int 值,而不是 0(ok)和 1(cancel)。

      因此您可能需要更精确地声明枚举类型并使用 rowValue。例如

      enum AlertResultType : Int {
          case ok = 0, cancel = 1
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-21
        • 1970-01-01
        • 2018-03-02
        • 1970-01-01
        相关资源
        最近更新 更多