10

我在 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

我完全不明白这一点。

4

3 回答 3

18

这是获取所有枚举值序列的一种未记录的方式,并且只能在早期的 Swift 版本中使用。它依赖于枚举值的散列值是连续整数,从零开始。

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

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

你可以用

print(NumberEnum.one.hashValue)
print(NumberEnum.two.hashValue)

不会打印0并且1使用 Xcode 10,但其他一些值也会随着每个程序的运行而变化。

有关正确的 Swift 4.2/Xcode 10 解决方案,请参阅如何使用 String 类型枚举枚举?

extension NumberEnum: CaseIterable  { }
print(Array(NumberEnum.allCases).count) // 4
于 2018-07-06T07:46:02.453 回答
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”

于 2019-02-06T07:39:39.453 回答
0

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

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

enum AlertResultType {
    case ok, cancel 
}

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

所以你可能需要更精确地声明枚举类型并使用rowValue。例如

enum AlertResultType : Int {
    case ok = 0, cancel = 1
}
于 2019-05-07T14:45:01.900 回答