Iterating through an Enum in Swift 3.0
我有一个简单的枚举,我想遍历它。为此,我采用了序列和迭代器协议,如下面的代码所示。顺便说一句,这可以复制/粘贴到xcode 8的操场上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import UIKit enum Sections: Int { case Section0 = 0 case Section1 case Section2 } extension Sections : Sequence { func makeIterator() -> SectionsGenerator { return SectionsGenerator() } struct SectionsGenerator: IteratorProtocol { var currentSection = 0 mutating func next() -> Sections? { guard let item = Sections(rawValue:currentSection) else { return nil } currentSection += 1 return item } } } for section in Sections { print(section) } |
但是for-in循环生成错误消息"type"部分。type"不符合协议"sequence"。协议一致性在我的扩展中;那么,这个代码有什么问题?
我知道还有其他方法可以做到这一点,但我想知道这种方法有什么问题。
谢谢。
请注意,Martin的解决方案可以重构为一个协议:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import Foundation protocol EnumSequence { associatedtype T: RawRepresentable where T.RawValue == Int static func all() -> AnySequence<T> } extension EnumSequence { static func all() -> AnySequence<T> { return AnySequence { return EnumGenerator() } } } private struct EnumGenerator<T: RawRepresentable>: IteratorProtocol where T.RawValue == Int { var index = 0 mutating func next() -> T? { guard let item = T(rawValue: index) else { return nil } index += 1 return item } } |
然后,给定一个枚举
1 2 3 | enum Fruits: Int { case apple, orange, pear } |
您将拍打协议和类型别名:
1 2 3 4 5 6 | enum Fruits: Int, EnumSequence { typealias T = Fruits case apple, orange, pear } Fruits.all().forEach({ print($0) }) // apple orange pear |
更新:从swift 4.2开始,您可以简单地添加协议一致性对于
您可以迭代符合
1 2 3 | for section in Sections.Section0 { print(section) } |
将编译并给出预期的结果。但那当然不是你真正想要的是什么,因为价值的选择是任意的,序列中不需要值本身。
据我所知,无法迭代类型本身,因此
1 2 3 | for section in Sections { print(section) } |
编译。这就要求"元类型"
您可以做的是定义一个返回序列的类型方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | extension Sections { static func all() -> AnySequence<Sections> { return AnySequence { return SectionsGenerator() } } struct SectionsGenerator: IteratorProtocol { var currentSection = 0 mutating func next() -> Sections? { guard let item = Sections(rawValue:currentSection) else { return nil } currentSection += 1 return item } } } for section in Sections.all() { print(section) } |
只需添加到枚举:
比你能做到的还要多:
1 2 3 | Sections.allTypes.forEach { (section) in print("\(section)") } |
这看起来简单多了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public protocol EnumSequence { init?(rawValue: Int) } public extension EnumSequence { public static var items: [Self] { var caseIndex: Int = 0 let interator: AnyIterator<Self> = AnyIterator { let result = Self(rawValue: caseIndex) caseIndex += 1 return result } return Array(interator) } } |
如果您的枚举是基于int的枚举,那么您可以执行这样一个有效但有点脏的技巧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | enum MyEnum: Int { case One case Two } extension MyEnum { func static allCases() -> [MyEnum] { var allCases = [MyEnum]() for i in 0..<10000 { if let type = MyEnum(rawValue: i) { allCases.append(type) } else { break } } return allCases } } |
然后循环myenum.allcases()。
迭代上述解决方案后,请参阅下面的一个协议,该协议可以通过枚举来实现,以添加allvalues序列,但也允许转换为字符串值或从字符串值转换。
对于需要支持目标C的类字符串枚举非常方便(这里只允许int枚举)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | public protocol ObjcEnumeration: LosslessStringConvertible, RawRepresentable where RawValue == Int { static var allValues: AnySequence<Self> { get } } public extension ObjcEnumeration { public static var allValues: AnySequence<Self> { return AnySequence { return IntegerEnumIterator() } } public init?(_ description: String) { guard let enumValue = Self.allValues.first(where: { $0.description == description }) else { return nil } self.init(rawValue: enumValue.rawValue) } public var description: String { return String(describing: self) } } fileprivate struct IntegerEnumIterator<T: RawRepresentable>: IteratorProtocol where T.RawValue == Int { private var index = 0 mutating func next() -> T? { defer { index += 1 } return T(rawValue: index) } } |
例如:
1 2 3 4 | @objc enum Fruit: Int, ObjcEnumeration { case apple, orange, pear } |
现在你可以做到:
1 2 3 4 5 6 7 8 9 | for fruit in Fruit.allValues { //Prints:"apple","orange","pear" print("Fruit: \(fruit.description)") if let otherFruit = Fruit(fruit.description), fruit == otherFruit { print("Fruit could be constructed successfully from its description!") } } |