我有一个简单的枚举,我想迭代。为此,我采用了Sequence和IteratorProtocol,如下面的代码所示。顺便说一句,这可以复制/粘贴到Xcode 8中的Playground。
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' Sections.Type'不符合协议'序列'" 。 协议一致性在我的扩展中;那么,这段代码出了什么问题?
我知道还有其他方法可以做到这一点,但我想了解这种方法的错误。
感谢。
答案 0 :(得分:10)
请注意,Martin’s solution可以重构为协议:
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
}
}
然后,给出一个枚举
enum Fruits: Int {
case apple, orange, pear
}
你打了协议和一个typealias:
enum Fruits: Int, EnumSequence {
typealias T = Fruits
case apple, orange, pear
}
Fruits.all().forEach({ print($0) }) // apple orange pear
答案 1 :(得分:8)
更新:从Swift 4.2开始,您只需添加协议一致性即可
到CaseIterable
,请参阅How to enumerate an enum with String type?。
您可以迭代符合Sequence
的类型的值
协议。因此
for section in Sections.Section0 {
print(section)
}
会编译并给出预期的结果。但当然不是 真的是你想要的,因为价值的选择是随意的 价值本身在序列中不需要。
据我所知,没有办法迭代一个类型本身,所以
for section in Sections {
print(section)
}
编译。那将需要&#34;元型#34; Sections.Type
符合
到Sequence
。也许有人证明我错了。
您可以做的是定义一个返回序列的类型方法:
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)
}
答案 2 :(得分:3)
这看起来简单得多:
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)
}
}
答案 3 :(得分:3)
只需添加到枚举:
static var allTypes: [Sections] = [.Section0, .Section1, .Section2]
而且您可以:
Sections.allTypes.forEach { (section) in
print("\(section)")
}
答案 4 :(得分:1)
迭代上面的解决方案,请参阅下面的一个协议,该协议可以通过枚举来实现,以添加allValues序列,但也允许转换为字符串值和从字符串值转换的可能性。
非常方便需要支持目标c的类似字符串的枚举(在那里只允许使用int枚举)。
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)
}
}
具体例子:
@objc
enum Fruit: Int, ObjcEnumeration {
case apple, orange, pear
}
现在你可以做到:
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!")
}
}
答案 5 :(得分:1)
如果你的枚举是基于Int的,你就可以做一个有效但有点肮脏的伎俩。
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()..