即使使用相同的快速版本,Xcode 9和Xcode 10也会产生不同的结果

时间:2018-07-06 07:35:43

标签: swift xcode xcode10

我正在xcode 9.3和xcode 10 beta 3游乐场中运行此代码

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

我一点都不明白。

3 个答案:

答案 0 :(得分:18)

这是一种获取所有枚举值序列的无证方法, 并且只能偶然地使用早期的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

答案 1 :(得分:1)

以下针对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”

答案 2 :(得分:0)

如果使用枚举的hashValue确定大小写值(位置或id),则这是错误的方法,因为它不能保证返回顺序的int值0,1,2。迅速4.2

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

enum AlertResultType {
    case ok, cancel 
}

此枚举的hashValue可能返回一个较大的int值,而不是0(确定)和1(取消)。

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

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