关于swift:如何用字符串类型枚举枚举枚举?

How to enumerate an enum with String type?

1
2
3
4
5
6
enum Suit: String {
    case spades ="?"
    case hearts ="?"
    case diamonds ="?"
    case clubs ="?"
}

例如,我如何执行以下操作:

1
2
3
4
for suit in Suit {
    // do something with suit
    print(suit.rawValue)
}

结果示例:

1
2
3
4
?
?
?
?


本帖与此相关:https://www.swift-studies.com/blog/2014/6/10/enumering-enums-in-swift

基本上,建议的解决方案是

1
2
3
4
5
6
7
8
9
enum ProductCategory : String {
     case Washers ="washers", Dryers ="dryers", Toasters ="toasters"

     static let allValues = [Washers, Dryers, Toasters]
}

for category in ProductCategory.allValues{
     //Do something
}


我做了一个实用函数iterateEnum(),用于迭代任意enum类型的案例。

以下是示例用法:

1
2
3
4
5
6
7
8
9
10
enum Suit:String {
    case Spades ="?"
    case Hearts ="?"
    case Diamonds ="?"
    case Clubs ="?"
}

for f in iterateEnum(Suit) {
    println(f.rawValue)
}

输出:

1
2
3
4
?
?
?
?

但是,这只是为了调试或测试:这依赖于几个未记录的当前(swift1.1)编译器行为。因此,使用它的风险由您自己承担:)

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func iterateEnum<T: Hashable>(_: T.Type) -> GeneratorOf<T> {
    var cast: (Int -> T)!
    switch sizeof(T) {
    case 0: return GeneratorOf(GeneratorOfOne(unsafeBitCast((), T.self)))
    case 1: cast = { unsafeBitCast(UInt8(truncatingBitPattern: $0), T.self) }
    case 2: cast = { unsafeBitCast(UInt16(truncatingBitPattern: $0), T.self) }
    case 4: cast = { unsafeBitCast(UInt32(truncatingBitPattern: $0), T.self) }
    case 8: cast = { unsafeBitCast(UInt64($0), T.self) }
    default: fatalError("cannot be here")
    }

    var i = 0
    return GeneratorOf {
        let next = cast(i)
        return next.hashValue == i++ ? next : nil
    }
}

基本理念是:

  • enum的记忆表示法(不包括enum及其相关类型)只是一种病例索引,当病例数为2...256时,它与UInt8相同,当257...65536时,它与UInt16等相同。因此,它可以是对应的无符号整数类型中的unsafeBitcast
  • 枚举值的.hashValue与案例的索引相同。
  • 从无效索引中发出的枚举值的.hashValue0

补充:

为Swift2进行了修订,并从@Kametrixom的答案中实现了铸造理念。

1
2
3
4
5
6
7
func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> {
    var i = 0
    return anyGenerator {
        let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }
        return next.hashValue == i++ ? next : nil
    }
}

补充:修改为swift3

1
2
3
4
5
6
7
8
9
10
11
func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
    var i = 0
    return AnyIterator {
        let next = withUnsafePointer(to: &i) {
            $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee }
        }
        if next.hashValue != i { return nil }
        i += 1
        return next
    }
}

补充:修改为swift3.0.1

1
2
3
4
5
6
7
8
9
func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
    var i = 0
    return AnyIterator {
        let next = withUnsafeBytes(of: &i) { $0.load(as: T.self) }
        if next.hashValue != i { return nil }
        i += 1
        return next
    }
}


斯威夫特4.2 +

从Swift 4.2(Xcode 10)开始,只需添加与CaseIterable的协议一致性,即可从allCases中获益:

1
extension Suit: CaseIterable {}

然后将打印所有可能的值:

1
2
3
Suit.allCases.forEach {
    print($0.rawValue)
}

与早期的swift版本(3.x和4.x)兼容

只需模拟swift 4.2的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#if !swift(>=4.2)
public protocol CaseIterable {
    associatedtype AllCases: Collection where AllCases.Element == Self
    static var allCases: AllCases { get }
}
extension CaseIterable where Self: Hashable {
    static var allCases: [Self] {
        return [Self](AnySequence { () -> AnyIterator<Self> in
            var raw = 0
            var first: Self?
            return AnyIterator {
                let current = withUnsafeBytes(of: &raw) { $0.load(as: Self.self) }
                if raw == 0 {
                    first = current
                } else if current == first {
                    return nil
                }
                raw += 1
                return current
            }
        })
    }
}
#endif


其他的解决方案也有效,但它们都假设了可能的等级和诉讼数量,或者第一个和最后一个等级可能是什么。诚然,在可预见的未来,一副牌的布局可能不会有太大变化。然而,一般来说,编写尽可能少做假设的代码比较整洁。我的解决方案:

我已经向suit枚举添加了一个原始类型,因此可以使用suit(rawvalue:)访问suit cases:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
enum Suit: Int {
    case Spades = 1
    case Hearts, Diamonds, Clubs
    func simpleDescription() -> String {
        switch self {
            case .Spades:
                return"spades"
            case .Hearts:
                return"hearts"
            case .Diamonds:
                return"diamonds"
            case .Clubs:
                return"clubs"
        }
    }
    func color() -> String {
        switch self {
        case .Spades:
            return"black"
        case .Clubs:
            return"black"
        case .Diamonds:
            return"red"
        case .Hearts:
            return"red"
        }
    }
}

enum Rank: Int {
    case Ace = 1
    case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
    case Jack, Queen, King
    func simpleDescription() -> String {
        switch self {
            case .Ace:
                return"ace"
            case .Jack:
                return"jack"
            case .Queen:
                return"queen"
            case .King:
                return"king"
            default:
                return String(self.rawValue)
        }
    }
}

在card的createDeck()方法的实现下面。init(rawvalue:)是一个可失败的初始值设定项,并返回一个可选的。通过拆解和检查这两个while语句中的值,无需假设排名或诉讼案例的数量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return"The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }
    func createDeck() -> [Card] {
        var n = 1
        var deck = [Card]()
        while let rank = Rank(rawValue: n) {
            var m = 1
            while let suit = Suit(rawValue: m) {
                deck.append(Card(rank: rank, suit: suit))
                m += 1
            }
            n += 1
        }
        return deck
    }
}

以下是如何调用CreateDeck方法:

1
2
let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
let deck = card.createDeck()


第二个真正有效的答案

因此,我在字节和位中发现了一些问题,并创建了一个扩展(后来我发现它的工作原理与@rintaro的答案非常相似)。它可以这样使用:

1
2
3
4
5
enum E : EnumCollection {
    case A, B, C
}

Array(E.cases())    // [A, B, C]

值得注意的是,它可以用于任何枚举(没有关联的值)。请注意,这对没有案例的枚举不起作用。

免责声明

与@rintaro的答案一样,此代码使用枚举的基础表示形式。这种表示法没有记录在案,将来可能会改变,这会破坏它->我不建议在生产中使用它。

代码(swift 2.2,xcode 7.3.1,不在xcode 10上工作)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protocol EnumCollection : Hashable {}
extension EnumCollection {
    static func cases() -> AnySequence<Self> {
        typealias S = Self
        return AnySequence { () -> AnyGenerator<S> in
            var raw = 0
            return AnyGenerator {
                let current : Self = withUnsafePointer(&raw) { UnsafePointer($0).memory }
                guard current.hashValue == raw else { return nil }
                raw += 1
                return current
            }
        }
    }
}

代码(swift 3,xcode 8.1,不在xcode 10上工作)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protocol EnumCollection : Hashable {}
extension EnumCollection {
    static func cases() -> AnySequence<Self> {
        typealias S = Self
        return AnySequence { () -> AnyIterator<S> in
            var raw = 0
            return AnyIterator {
                let current : Self = withUnsafePointer(to: &raw) { $0.withMemoryRebound(to: S.self, capacity: 1) { $0.pointee } }
                guard current.hashValue == raw else { return nil }
                raw += 1
                return current
            }
        }
    }
}

(我不知道为什么我需要typealias,但是编译器抱怨没有它)

(我对这个答案做了很大修改,看看以前版本的编辑)


您可以通过实现ForwardIndexType协议来迭代枚举。

ForwardIndexType协议要求您定义一个successor()函数来逐步遍历元素。

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
33
34
35
enum Rank: Int, ForwardIndexType {
    case Ace = 1
    case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
    case Jack, Queen, King

    // ... other functions

    // Option 1 - Figure it out by hand
    func successor() -> Rank {
        switch self {
            case .Ace:
              return .Two
            case .Two:
              return .Three

            // ... etc.

            default:
              return .King
        }
    }

    // Option 2 - Define an operator!
    func successor() -> Rank {
        return self + 1
    }
}

// NOTE: The operator is defined OUTSIDE the class
func + (left: Rank, right: Int) -> Rank {
    // I'm using to/from raw here, but again, you can use a case statement
    // or whatever else you can think of

    return left == .King ? .King : Rank(rawValue: left.rawValue + right)!
}

在打开或关闭的范围内迭代(..<...将在内部调用successor()函数,该函数允许您编写:

1
2
3
4
// Under the covers, successor(Rank.King) and successor(Rank.Ace) are called to establish limits
for r in Rank.Ace...Rank.King {
    // Do something useful
}


原则上,可以这样做,前提是不为枚举的情况使用原始值分配:

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
enum RankEnum: Int {
  case Ace
  case One
  case Two
}

class RankEnumGenerator : Generator {
  var i = 0
  typealias Element = RankEnum
  func next() -> Element? {
    let r = RankEnum.fromRaw(i)
    i += 1
    return r
  }
}

extension RankEnum {
  static func enumerate() -> SequenceOf<RankEnum> {
    return SequenceOf<RankEnum>({ RankEnumGenerator() })
  }
}

for r in RankEnum.enumerate() {
  println("\(r.toRaw())")
}


这个问题现在容易多了。这是我的Swift 4.2解决方案。

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
enum Suit: Int, CaseIterable {
    case None
    case Spade, Heart, Diamond, Club

    static let allNonNullCases = Suit.allCases[Spade.rawValue...]
}

enum Rank: Int, CaseIterable {
    case Joker
    case Two, Three, Four, Five, Six, Seven, Eight
    case Nine, Ten, Jack, Queen, King, Ace

    static let allNonNullCases = Rank.allCases[Two.rawValue...]
}

func makeDeck(withJoker: Bool = false) -> [Card] {
    var deck = [Card]()
    for suit in Suit.allNonNullCases {
        for rank in Rank.allNonNullCases {
            deck.append(Card(suit: suit, rank: rank))
        }
    }
    if withJoker {
        deck.append(Card(suit: .None, rank: .Joker))
    }
    return deck
}

前4.2

我喜欢这个解决方案,在找到这个页面后我把它放在一起:斯威夫特列表理解

它使用int raws而不是字符串,但它避免键入两次,允许自定义范围,并且不硬编码原始值。

这是我最初的解决方案的Swift 4版本,但请参阅上面的4.2改进。

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
enum Suit: Int {
    case None
    case Spade, Heart, Diamond, Club

    static let allRawValues = Suit.Spade.rawValue...Suit.Club.rawValue
    static let allCases = Array(allRawValues.map{ Suit(rawValue: $0)! })
}
enum Rank: Int {
    case Joker
    case Two, Three, Four, Five, Six
    case Seven, Eight, Nine, Ten
    case Jack, Queen, King, Ace

    static let allRawValues = Rank.Two.rawValue...Rank.Ace.rawValue
    static let allCases = Array(allRawValues.map{ Rank(rawValue: $0)! })
}
func makeDeck(withJoker: Bool = false) -> [Card] {
    var deck = [Card]()
    for suit in Suit.allCases {
        for rank in Rank.allCases {
            deck.append(Card(suit: suit, rank: rank))
        }
    }
    if withJoker {
        deck.append(Card(suit: .None, rank: .Joker))
    }
    return deck
}


如果给枚举一个原始int值,它将使循环更容易。

例如,您可以使用anyGenerator获得一个生成器,该生成器可以枚举您的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
enum Suit: Int, CustomStringConvertible {
    case Spades, Hearts, Diamonds, Clubs
    var description: String {
        switch self {
        case .Spades:   return"Spades"
        case .Hearts:   return"Hearts"
        case .Diamonds: return"Diamonds"
        case .Clubs:    return"Clubs"
        }
    }
    static func enumerate() -> AnyGenerator<Suit> {
        var nextIndex = Spades.rawValue
        return anyGenerator { Suit(rawValue: nextIndex++) }
    }
}
// You can now use it like this:
for suit in Suit.enumerate() {
    suit.description
}
// or like this:
let allSuits: [Suit] = Array(Suit.enumerate())

然而,这看起来是一个相当常见的模式,如果我们可以通过简单地符合协议使任何枚举类型可枚举,这不是很好吗?有了Swift2.0和协议扩展,现在我们可以了!

只需将此添加到项目中:

1
2
3
4
5
6
7
8
9
10
11
protocol EnumerableEnum {
    init?(rawValue: Int)
    static func firstValue() -> Int
}
extension EnumerableEnum {
    static func enumerate() -> AnyGenerator<Self> {
        var nextIndex = firstRawValue()
        return anyGenerator { Self(rawValue: nextIndex++) }
    }
    static func firstRawValue() -> Int { return 0 }
}

现在,无论何时创建枚举(只要它具有int原始值),都可以通过符合协议使其可枚举:

1
2
3
4
5
enum Rank: Int, EnumerableEnum {
    case Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King
}
// ...
for rank in Rank.enumerate() { ... }

如果枚举值不是以0开头(默认值),则重写firstRawValue方法:

1
2
3
4
5
6
enum DeckColor: Int, EnumerableEnum {
    case Red = 10, Blue, Black
    static func firstRawValue() -> Int { return Red.rawValue }
}
// ...
let colors = Array(DeckColor.enumerate())

最终的诉讼类别,包括用更标准的customstringconvertible协议替换simpleDescription,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum Suit: Int, CustomStringConvertible, EnumerableEnum {
    case Spades, Hearts, Diamonds, Clubs
    var description: String {
        switch self {
        case .Spades:   return"Spades"
        case .Hearts:   return"Hearts"
        case .Diamonds: return"Diamonds"
        case .Clubs:    return"Clubs"
        }
    }
}
// ...
for suit in Suit.enumerate() {
    print(suit.description)
}

编辑:

Swift 3语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protocol EnumerableEnum {
    init?(rawValue: Int)
    static func firstRawValue() -> Int
}

extension EnumerableEnum {
    static func enumerate() -> AnyIterator<Self> {
        var nextIndex = firstRawValue()

        let iterator: AnyIterator<Self> = AnyIterator {
            defer { nextIndex = nextIndex + 1 }
            return Self(rawValue: nextIndex)
        }

        return iterator
    }

    static func firstRawValue() -> Int {
        return 0
    }
}


更新至Swift 2.2+

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> {
    var i = 0
    return AnyGenerator {
        let next = withUnsafePointer(&i) {
            UnsafePointer<T>($0).memory
        }
        if next.hashValue == i {
            i += 1
            return next
        } else {
            return nil
        }
    }
}

它将代码更新为swift 2.2表格@kametrixom的答案

对于Swift 3.0+(非常感谢@Philip)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
    var i = 0
    return AnyIterator {
        let next = withUnsafePointer(&i) {
            UnsafePointer<T>($0).pointee
        }
        if next.hashValue == i {
            i += 1
            return next
        } else {
            return nil
        }
    }
}


我发现自己在代码中做了很多。我最终找到了一种简单地符合Iteratable协议并使用rawValues()方法的方法。

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
33
34
35
36
protocol Iteratable {}
extension RawRepresentable where Self: RawRepresentable {

    static func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
        var i = 0
        return AnyIterator {
            let next = withUnsafePointer(to: &i) {
                $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee }
            }
            if next.hashValue != i { return nil }
            i += 1
            return next
        }
    }
}

extension Iteratable where Self: RawRepresentable, Self: Hashable {
    static func hashValues() -> AnyIterator<Self> {
        return iterateEnum(self)
    }

    static func rawValues() -> [Self.RawValue] {
        return hashValues().map({$0.rawValue})
    }
}


// Example
enum Grocery: String, Iteratable {
    case Kroger ="kroger"
    case HEB ="h.e.b."
    case Randalls ="randalls"
}

let groceryHashes = Grocery.hashValues() // AnyIterator<Grocery>
let groceryRawValues = Grocery.rawValues() // ["kroger","h.e.b.","randalls"]

编辑:Swift Evolution Proposal SE-0194派生的枚举案例集合为这个问题提出了一个平庸的解决方案。我们在Swift 4.2和更新版本中看到它。该提案还指出了一些类似于这里已经提到过的一些解决方法,但可能会有趣地看到。

为了完整起见,我也会保留我原来的职位。

这是另一种基于@peymmankh答案的方法,适用于Swift 3。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public protocol EnumCollection : Hashable {}

extension EnumCollection {

public static func allValues() -> [Self] {
    typealias S = Self

    let retVal = AnySequence { () -> AnyIterator<S> in
        var raw = 0
        return AnyIterator {
            let current = withUnsafePointer(to: &raw) {
                 $0.withMemoryRebound(to: S.self, capacity: 1) { $0.pointee }
            }
            guard current.hashValue == raw else { return nil }
            raw += 1
            return current
        }
    }

    return [S](retVal)
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
enum Rank: Int {
    ...
    static let ranks = (Rank.Ace.rawValue ... Rank.King.rawValue).map{Rank(rawValue: $0)! }

}
enum Suit {
    ...
    static let suits = [Spades, Hearts, Diamonds, Clubs]
}

struct Card {
    ...
    static func fullDesk() -> [Card] {
        var desk: [Card] = []
        for suit in Suit.suits {
            for rank in Rank.ranks {
                desk.append(Card(rank: rank,suit: suit))
            }
        }
        return desk
    }
}

这个怎么样?


在swift 3中,当基础枚举具有rawvalue_s时,可以实现可跨接协议。其优点是,没有像其他一些建议中那样创建值数组,并且标准的swift"for i in…"语句工作正常,这使得语法更加良好。

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
33
34
35
36
37
//"Int" to get rawValue, and {Strideable} so we can iterate
enum MyColorEnum : Int, Strideable {
    case Red
    case Green
    case Blue
    case Black

    //-------- required by {Strideable}
    typealias Stride = Int

    func advanced(by n:Stride) -> MyColorEnum {
        var next = self.rawValue + n
        if next > MyColorEnum.Black.rawValue {
            next = MyColorEnum.Black.rawValue
        }
        return MyColorEnum(rawValue: next)!
    }

    func distance(to other: MyColorEnum) -> Int {
        return other.rawValue - self.rawValue
    }

    //-------- just for printing
    func simpleDescription() -> String {
        switch self {
        case .Red: return"Red"
        case .Green: return"Green"
        case .Blue: return"Blue"
        case .Black: return"Black"
        }
    }
}

// this is how you use it:
for i in MyColorEnum.Red ... MyColorEnum.Black {
    print("ENUM: \(i)")
}


这就是我最终要做的;我认为它在可读性和可维护性之间达到了正确的平衡。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct Card {

// ...

static func deck() -> Card[] {
    var deck = Card[]()
    for rank in Rank.Ace.toRaw()...Rank.King.toRaw() {
        for suit in [Suit.Spades, .Hearts, .Clubs, .Diamonds] {
            let card = Card(rank: Rank.fromRaw(rank)!, suit: suit)
            deck.append(card)
        }
    }
    return deck
}

let deck = Card.deck()


你可以这样列举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
enum Planet: String {
  case Mercury
  case Venus
  case Earth
  case Mars

static var enumerate: [Planet] {
    var a: [Planet] = []
    switch Planet.Mercury {
    case .Mercury: a.append(.Mercury); fallthrough
    case .Venus: a.append(.Venus); fallthrough
    case .Earth: a.append(.Earth); fallthrough
    case .Mars: a.append(.Mars)
    }
    return a
  }
}

Planet.enumerate // [Mercury, Venus, Earth, Mars]


抱歉,我的回答是针对我如何在我需要做的事情中使用这篇文章的。对于那些偶然发现这个问题的人,寻找在枚举中查找案例的方法,这是一种方法(swift 2中的新方法):

编辑:小写camelcase现在是swift 3枚举值的标准

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// From apple docs: If the raw-value type is specified as String and you don’t assign values to the cases explicitly, each unassigned case is implicitly assigned a string with the same text as the name of that case.

enum Theme: String
    {
    case white, blue, green, lavender, grey
    }

func loadTheme(theme: String)
    {
    // this checks the string against the raw value of each enum case (note that the check could result in a nil value, since it's an optional, which is why we introduce the if/let block
    if let testTheme = Theme(rawValue: theme)
        {
        // testTheme is guaranteed to have an enum value at this point
        self.someOtherFunction(testTheme)
        }
    }

对于那些对枚举枚举感到疑惑的人,本页中给出的包含包含所有枚举值数组的静态var/let的答案是正确的。苹果最新的TVOS示例代码包含了完全相同的技术。

也就是说,他们应该在语言中建立一个更方便的机制(苹果,你在听吗?)!


带Swift 4.2的Xcode 10

1
2
3
4
5
6
7
8
9
10
enum Filter: String, CaseIterable {

    case salary ="Salary"
    case experience ="Experience"
    case technology ="Technology"
    case unutilized ="Unutilized"
    case unutilizedHV ="Unutilized High Value"

    static let allValues = Filter.allCases.map { $0.rawValue }
}

称之为

1
print(Filter.allValues)

印刷品:

["Salary","Experience","Technology","Unutilized","Unutilized High Value"]

旧版本代表Intenum

1
2
3
4
5
6
7
8
9
10
enum Filter: Int {
    case salary
    case experience
    case technology
    case unutilized
    case unutilizedHV

    static let allRawValues = salary.rawValue...unutilizedHV.rawValue  // First to last case
    static let allValues = allRawValues.map { Filter(rawValue: $0)!.rawValue }
}

这样称呼它:

1
print(Filter.allValues)

印刷品:

[0, 1, 2, 3, 4]

代表Stringenum

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
enum Filter: Int {
    case salary
    case experience
    case technology
    case unutilized
    case unutilizedHV

    static let allRawValues = salary.rawValue...unutilizedHV.rawValue  // First to last case
    static let allValues = allRawValues.map { Filter(rawValue: $0)!.description }
}

extension Filter: CustomStringConvertible {
    var description: String {
        switch self {
        case .salary: return"Salary"
        case .experience: return"Experience"
        case .technology: return"Technology"
        case .unutilized: return"Unutilized"
        case .unutilizedHV: return"Unutilized High Value"
        }
    }
}

称之为

1
print(Filter.allValues)

印刷品:

["Salary","Experience","Technology","Unutilized","Unutilized High Value"]


实验是:实验

在卡片中添加一种方法,可以创建一副完整的卡片,其中每一张卡片都是等级和套装的组合。

因此,在不修改或增强给定代码的情况下,除了添加方法(并且不使用尚未教授过的内容),我提出了以下解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Card {
    var rank: Rank
    var suit: Suit

    func simpleDescription() -> String {
        return"The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }

    func createDeck() -> [Card] {
        var deck: [Card] = []
        for rank in Rank.Ace.rawValue...Rank.King.rawValue {
            for suit in Suit.Spades.rawValue...Suit.Clubs.rawValue {
                let card = Card(rank: Rank(rawValue: rank)!, suit: Suit(rawValue: suit)!)
                //println(card.simpleDescription())
                deck += [card]
            }
        }
        return deck
    }
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
let deck = threeOfSpades.createDeck()

这看起来像是一个黑客,但是如果你使用原始值,你可以这样做。

1
2
3
4
5
6
7
8
9
enum Suit: Int {  
    case Spades = 0, Hearts, Diamonds, Clubs  
 ...  
}  

var suitIndex = 0  
while var suit = Suit.fromRaw(suitIndex++) {  
   ...  
}

枚举具有toraw()和fromraw()方法,因此如果原始值是int,则可以从第一个枚举迭代到最后一个枚举:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
enum Suit: Int {
    case Spades = 1
    case Hearts, Diamonds, Clubs
    func simpleDescription() -> String {
        switch self {
        case .Spades:
            return"spades"
        case .Hearts:
            return"hearts"
        case .Diamonds:
            return"diamonds"
        case .Clubs:
            return"clubs"
        }
    }
}

for i in Suit.Spades.toRaw()...Suit.Clubs.toRaw() {
    if let covertedSuit = Suit.fromRaw(i) {
        let description = covertedSuit.simpleDescription()
    }
}

其中一个问题是,在运行simpleDescription方法之前需要测试可选值,因此我们首先将convertedSuit设置为我们的值,然后将常量设置为convertedSuit.simpleDescription()。


这是我建议的方法。这并不完全令人满意(我对斯威夫特和OOP很陌生!)但也许有人能改进它。其思想是让每个枚举以.First和.Last属性的形式提供自己的范围信息。它只向每个枚举添加两行代码:仍然有点硬编码,但至少它没有复制整个集合。它确实需要将suit枚举修改为int,就像rank枚举一样,而不是非untyped。

这里是我添加到rank枚举中的代码,它位于case语句之后(suit枚举类似),而不是返回整个解决方案:

1
2
var first: Int { return Ace.toRaw() }
var last: Int { return King.toRaw() }

我用来构建甲板作为字符串数组的循环。(问题定义没有说明甲板的结构。)

1
2
3
4
5
6
7
8
9
10
11
func createDeck() -> [String] {
var deck: [String] = []
var card: String
for r in Rank.Ace.first...Rank.Ace.last {
    for s in Suit.Hearts.first...Suit.Hearts.last {
       card = Rank.simpleDescription( Rank.fromRaw(r)!)() +" of" + Suit.simpleDescription( Suit.fromRaw(s)!)()
       deck.append( card)
       }
   }
return deck
}

这是不令人满意的,因为属性与元素而不是枚举相关联。但它确实为"for"循环增加了清晰度。我想说的是rank.first而不是rank.ace.first。它(对任何元素)都有效,但很难看。有人能演示如何将其提升到枚举级别吗?

为了让它工作,我把createDeck方法从card结构中提取出来…无法确定如何从该结构中获取返回的[string]数组,这似乎是放置此类方法的不好地方。


我使用了computed属性,它返回所有值的数组(感谢本文http://natecook.com/blog/2014/10/loopy random enum ideas/)。但是它也使用int raw值,但我不需要在单独的属性中重复枚举的所有成员。

更新xcode6.1有点改变了如何使用原始值获取枚举成员的方法,所以我修复了清单。还修复了第一个原始值错误的小错误

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
enum ValidSuits:Int{
    case Clubs=0, Spades, Hearts, Diamonds
    func description()->String{
        switch self{
        case .Clubs:
            return"??"
        case .Spades:
            return"??"
        case .Diamonds:
            return"??"
        case .Hearts:
            return"??"
        }
    }

    static var allSuits:[ValidSuits]{
        return Array(
            SequenceOf {
                () -> GeneratorOf<ValidSuits> in
                var i=0
                return GeneratorOf<ValidSuits>{
                    return ValidSuits(rawValue: i++)
                }
            }
        )
    }
}

另一个解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
enum Suit: String {
    case spades ="?"
    case hearts ="?"
    case diamonds ="?"
    case clubs ="?"

    static var count: Int {
        return 4  
    }

    init(index: Int) {
        switch index {
            case 0: self = .spades
            case 1: self = .hearts
            case 2: self = .diamonds
            default: self = .clubs
        }
    }
}

for i in 0..<Suit.count {
    print(Suit(index: i).rawValue)
}


在处理Swift 2.0时,我的建议是:

我在Suitenum中添加了原始类型。

1
enum Suit: Int {

然后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Card {
    var rank: Rank
    var suit: Suit


    func fullDeck()-> [Card] {

        var deck = [Card]()

        for i in Rank.Ace.rawValue...Rank.King.rawValue {

            for j in Suit.Spades.rawValue...Suit.Clubs.rawValue {

                deck.append(Card(rank:Rank(rawValue: i)! , suit: Suit(rawValue: j)!))
            }
        }

        return deck
    }
}

这里,我使用一个方法来迭代一个枚举,并从一个枚举中提供多个值类型

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
enum IterateEnum: Int {
    case Zero
    case One
    case Two
    case Three
    case Four
    case Five
    case Six
    case Seven

    //tuple allows multiple values to be derived from the enum case, and
    //since it is using a switch with no default, if a new case is added,
    //a compiler error will be returned if it doesn't have a value tuple set
    var value: (french:String, spanish:String, japanese:String) {
        switch self {
        case .Zero: return (french:"zéro", spanish:"cero", japanese:"nuru")
        case .One: return (french:"un", spanish:"uno", japanese:"ichi")
        case .Two: return (french:"deux", spanish:"dos", japanese:"ni")
        case .Three: return (french:"trois", spanish:"tres", japanese:"san")
        case .Four: return (french:"quatre", spanish:"cuatro", japanese:"shi")
        case .Five: return (french:"cinq", spanish:"cinco", japanese:"go")
        case .Six: return (french:"six", spanish:"seis", japanese:"roku")
        case .Seven: return (french:"sept", spanish:"siete", japanese:"shichi")
        }
    }

    //Used to iterate enum or otherwise access enum case by index order.
    //Iterate by looping until it returns nil
    static func item(index:Int) -> IterateEnum? {
        return IterateEnum.init(rawValue: index)
    }

    static func numberFromSpanish(number:String) -> IterateEnum? {
        return findItem { $0.value.spanish == number }
    }

    //use block to test value property to retrieve the enum case        
    static func findItem(predicate:((_:IterateEnum)->Bool)) -> IterateEnum? {

        var enumIndex:Int = -1
        var enumCase:IterateEnum?

        //Iterate until item returns nil
        repeat {
            enumIndex += 1
            enumCase = IterateEnum.item(index: enumIndex)

            if let eCase = enumCase {

                if predicate(eCase) {
                    return eCase
                }
            }
        } while enumCase != nil
        return nil
    }
}

var enumIndex:Int = -1
var enumCase:IterateEnum?

//Iterate until item returns nil
repeat {
    enumIndex += 1
    enumCase = IterateEnum.item(index: enumIndex)
    if let eCase = enumCase {
        print("The number \(eCase) in french: \(eCase.value.french), spanish: \(eCase.value.spanish), japanese: \(eCase.value.japanese)")
    }
} while enumCase != nil

print("Total of \(enumIndex) cases")

let number = IterateEnum.numberFromSpanish(number:"siete")

print("siete in japanese: \((number?.value.japanese ??"Unknown"))")

这是输出:

法语中的数字零:z_ro,西班牙语:cero,日语:nuru法语第一:联合国,西班牙语:联合国,日语:一法语排名第二:双人舞,西班牙语:dos,日语:ni法语排名第三:特洛伊语,西班牙语:tres,日语:san法语第四名:quatre,西班牙语:cuatro,日语:shi法语第五名:cinq,西班牙语:cinco,日语:go法语六号:六,西班牙语:塞浦路斯,日语:韩国法语排名第七:九月,西班牙语:锡特,日语:志

共8例

日文Siete:Shichi

更新

我最近创建了一个处理枚举的协议。协议需要一个具有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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
protocol EnumIteration {

    //Used to iterate enum or otherwise access enum case by index order. Iterate by looping until it returns nil

    static func item(index:Int) -> Self?
    static func iterate(item:((index:Int, enumCase:Self)->()), completion:(()->())?) {
    static func findItem(predicate:((enumCase:Self)->Bool)) -> Self?
    static func count() -> Int
}

extension EnumIteration where Self: RawRepresentable, Self.RawValue == Int {

    //Used to iterate enum or otherwise access enum case by index order. Iterate by looping until it returns nil
    static func item(index:Int) -> Self? {
        return Self.init(rawValue: index)
    }

    static func iterate(item:((index:Int, enumCase:Self)->()), completion:(()->())?) {

        var enumIndex:Int = -1
        var enumCase:Self?

        //Iterate until item returns nil
        repeat {
            enumIndex += 1
            enumCase = Self.item(enumIndex)

            if let eCase = enumCase {
                item(index: enumIndex, enumCase: eCase)
            }
        } while enumCase != nil
        completion?()
    }

    static func findItem(predicate:((enumCase:Self)->Bool)) -> Self? {

        var enumIndex:Int = -1
        var enumCase:Self?

        //Iterate until item returns nil
        repeat {
            enumIndex += 1
            enumCase = Self.item(enumIndex)

            if let eCase = enumCase {

                if predicate(enumCase:eCase) {
                    return eCase
                }
            }
        } while enumCase != nil
        return nil
    }

    static func count() -> Int {
        var enumIndex:Int = -1
        var enumCase:Self?

        //Iterate until item returns nil
        repeat {
            enumIndex += 1
            enumCase = Self.item(enumIndex)
        } while enumCase != nil

        //last enumIndex (when enumCase == nil) is equal to the enum count
        return enumIndex
    }
}

这是一个相当古老的帖子,来自Swift2.0。现在有一些更好的解决方案使用了Swift 3.0的新功能:在swift 3.0中迭代枚举

在这个问题上,有一个解决方案使用了Swift4.2的一个新特性(在我写这个编辑的时候还没有发布):如何获得swift枚举的计数?

这个线程和其他线程中有很多好的解决方案,但是其中一些非常复杂。我喜欢尽可能简化。这里有一个解决方案,可能适用于不同的需求,也可能不适用于不同的需求,但我认为它在大多数情况下都能很好地工作:

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
33
34
35
36
37
38
enum Number: String {
    case One
    case Two
    case Three
    case Four
    case EndIndex

    func nextCase () -> Number
    {
        switch self {
        case .One:
            return .Two
        case .Two:
            return .Three
        case .Three:
            return .Four
        case .Four:
            return .EndIndex

        /*
        Add all additional cases above
        */
        case .EndIndex:
            return .EndIndex
        }
    }

    static var allValues: [String] {
        var array: [String] = Array()
        var number = Number.One

        while number != Number.EndIndex {
            array.append(number.rawValue)
            number = number.nextCase()
        }
        return array
    }
}

迭代:

1
2
3
for item in Number.allValues {
    print("number is: \(item)")
}


正如这里的@kametrixom answer一样,我相信返回数组比返回任何序列都要好,因为您可以访问数组的所有优点,如count等。

这是重写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public protocol EnumCollection : Hashable {}
extension EnumCollection {
    public static func allValues() -> [Self] {
        typealias S = Self
        let retVal = AnySequence { () -> AnyGenerator<S> in
            var raw = 0
            return AnyGenerator {
                let current : Self = withUnsafePointer(&raw) { UnsafePointer($0).memory }
                guard current.hashValue == raw else { return nil }
                raw += 1
                return current
            }
        }

        return [S](retVal)
    }
}


(Karthik Kumar答案的改进)

这个解决方案使用编译器来保证您不会错过任何案例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum Suit: String {
    case spades ="?"
    case hearts ="?"
    case diamonds ="?"
    case clubs ="?"

    static var enumerate: [Suit] {
        switch Suit.spades {
        // make sure the two lines are identical ^_^
        case        .spades, .hearts, .diamonds, .clubs:
            return [.spades, .hearts, .diamonds, .clubs]
        }
    }
}

我使用了下面的方法,假设我知道排名枚举中的最后一个值,并且所有排名在ace之后都有增量值。

我喜欢这样,因为它又干净又小,容易理解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 func cardDeck() -> Card[] {
    var cards: Card[] = []
    let minRank = Rank.Ace.toRaw()
    let maxRank = Rank.King.toRaw()

    for rank in minRank...maxRank {
        if var convertedRank: Rank = Rank.fromRaw(rank) {
            cards.append(Card(rank: convertedRank, suite: Suite.Clubs))
            cards.append(Card(rank: convertedRank, suite: Suite.Diamonds))
            cards.append(Card(rank: convertedRank, suite: Suite.Hearts))
            cards.append(Card(rank: convertedRank, suite: Suite.Spades))
        }
    }

    return cards
}

有一个聪明的方法,令人沮丧,因为它说明了两种不同的枚举之间的区别。

试试这个:

1
2
3
4
5
6
7
8
9
10
    func makeDeck() -> Card[] {
      var deck: Card[] = []
      var suits: Suit[] = [.Hearts, .Diamonds, .Clubs, .Spades]
      for i in 1...13 {
        for suit in suits {
          deck += Card(rank: Rank.fromRaw(i)!, suit: suit)
        }
      }
      return deck
    }

协议是,由数字(原始值)支持的枚举是隐式显式排序的,而不由数字支持的枚举是隐式无序的。

例如,当我们给出枚举值数字时,语言就足够巧妙地计算出数字的顺序。另一方面,如果我们不给它任何排序,当我们试图迭代这些值时,语言就会把它的手举到空中,然后说"是的,但是你想先去哪一个?"??"

可以这样做的其他语言(迭代无序的枚举)可能是相同的语言,其中所有东西都是"在引擎盖下"的,实际上是一个映射或字典,并且您可以迭代映射的键,不管是否有任何逻辑顺序。

所以诀窍是为它提供一些显式排序的东西,在本例中是按照我们想要的顺序排列数组中的西服实例。一旦你这么做了,斯威夫特就会说:"那你为什么不先这么说呢?"

另一个速记技巧是在fromraw函数上使用forcing操作符。这说明了另一个关于枚举的"gotcha",即传递的可能值的范围通常大于枚举的范围。例如,如果我们说rank.fromraw(60),就不会返回值,所以我们使用语言的可选功能,在这里我们开始使用选项,随后将强制执行。(或者,如果让我来建造,我觉得还是有点奇怪)


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
33
34
35
36
37
38
39
40
enum Rank: Int
{
    case Ace = 0
    case Two, Three, Four, Five, Six, Seve, Eight, Nine, Ten
    case Jack, Queen, King
    case Count
}

enum Suit : Int
{
    case Spades = 0
    case Hearts, Diamonds, Clubs
    case Count
}

struct Card
{
    var rank:Rank
    var suit:Suit
}

class Test
{
    func makeDeck() -> Card[]
    {
        let suitsCount:Int = Suit.Count.toRaw()
        let rankCount:Int = Rank.Count.toRaw()
        let repeatedCard:Card = Card(rank:Rank.Ace, suit:Suit.Spades)
        let deck:Card[] = Card[](count:suitsCount*rankCount, repeatedValue:repeatedCard)

        for i:Int in 0..rankCount
        {
            for j:Int in 0..suitsCount
            {
                deck[i*suitsCount+j] = Card(rank: Rank.fromRaw(i)!, suit: Suit.fromRaw(j)!)
            }
        }
        return deck
    }
}

根据瑞克的回答:这比以前快了5倍


它花费了我更多的时间,然后只是结构中的一个方法,比如swift book调用的方法,但是我在枚举中设置了下一个函数。我本可以使用一个协议的,我不知道为什么,但把等级设置为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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
enum Rank: Int {
    case Ace = 1
    case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
    case Jack, Queen, King
    func simpleDescription() -> String {
        switch self{
        case .Ace:
            return"ace"
        case .Jack:
            return"jack"
        case .Queen:
            return"Queen"
        case .King:
            return"King"
        default:
            return String(self.toRaw())
        }
    }
    mutating func next() -> Rank {
        var rank = self
        var rawrank = rank.toRaw()
        var nrank:Rank = self
        rawrank = rawrank + 1
        if let newRank = Rank.fromRaw(rawrank) {
            println("\(newRank.simpleDescription())")
            nrank = newRank
        } else {
            return self
        }
        return nrank
    }
}

enum Suit {
    case Spades, Hearts, Diamonds, Clubs
    func color() -> String {
        switch self{
        case .Spades, .Clubs:
            return"black"
        default:
            return"red"
        }
    }
    func simpleDescription() -> String {
        switch self{
        case .Spades:
            return"spades"
        case .Hearts:
            return"hearts"
        case .Diamonds:
            return"diamonds"
        case .Clubs:
            return"clubs"
        }
    }
    mutating func next() -> Suit {
        switch self{
        case .Spades:
            return Hearts
        case .Hearts:
            return Diamonds
        case .Diamonds:
            return Clubs
        case .Clubs:
            return Spades
        }
    }
}

struct Card {
    var rank:Rank
    var suit:Suit
    func deck() -> Card[] {
        var tRank = self.rank
        var tSuit = self.suit
        let tcards = 52 // we start from 0
        var cards: Card[] = []
        for i in 0..tcards{
            var card = Card(rank: tRank, suit: tSuit)
            cards.append(card)
            tRank = tRank.next()
            tSuit = tSuit.next()
        }
        return cards
    }
    func simpleDescription() -> String {
        return"The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }
}

var card = Card(rank: .Ace, suit: .Spades)
var deck = card.deck()

希望这基本上有助于我使用一些一般知识,但可以通过将套装乘以等级(如果你不使用标准卡片组,你必须相应地更改枚举,如果基本上只是通过不同的枚举来节省时间,我使用等级原始值,如果你想要的,但是这个例子没有,所以我决定在不改变原值的情况下解决它。


我发现了一种有点刺耳的感觉,但这样做更安全,不需要输入两次值或引用枚举值的内存,使得它不太可能被破坏。

基本上,不要使用枚举,而是使用一个实例创建一个结构,并将所有枚举值设置为常量。然后可以使用Mirror查询变量。

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
public struct Suit{

    // the values
    let spades ="?"
    let hearts ="?"
    let diamonds ="?"
    let clubs ="?"

    // make a single instance of the Suit struct, Suit.instance
    struct SStruct{static var instance: Suit = Suit()}
    static var instance : Suit{
        get{return SStruct.instance}
        set{SStruct.instance = newValue}
    }

    // an array with all of the raw values
    static var allValues: [String]{
        var values = [String]()

        let mirror = Mirror(reflecting: Suit.instance)
        for (_, v) in mirror.children{
            guard let suit = v as? String else{continue}
            values.append(suit)
        }

        return values
    }
}

如果使用此方法,要获得单个值,需要使用Suit.instance.clubsSuit.instance.spades

但所有这些都很无聊…让我们做一些事情,让它更像一个真正的枚举!

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public struct SuitType{

    // store multiple things for each suit
    let spades = Suit("?", order: 4)
    let hearts = Suit("?", order: 3)
    let diamonds = Suit("?", order: 2)
    let clubs = Suit("?", order: 1)

    struct SStruct{static var instance: SuitType = SuitType()}
    static var instance : SuitType{
        get{return SStruct.instance}
        set{SStruct.instance = newValue}
    }

    // a dictionary mapping the raw values to the values
    static var allValuesDictionary: [String : Suit]{
        var values = [String : Suit]()

        let mirror = Mirror(reflecting: SuitType.instance)
        for (_, v) in mirror.children{
            guard let suit = v as? Suit else{continue}
            values[suit.rawValue] = suit
        }

        return values
    }
}

public struct Suit: RawRepresentable, Hashable{
    public var rawValue: String
    public typealias RawValue = String

    public var hashValue: Int{
        // find some integer that can be used to uniquely identify
        // each value. In this case, we could have used the order
        // variable because it is a unique value, yet to make this
        // apply to more cases, the hash table address of rawValue
        // will be returned, which should work in almost all cases
        //
        // you could also add a hashValue parameter to init() and
        // give each suit a different hash value
        return rawValue.hash
    }

    public var order: Int
    public init(_ value: String, order: Int){
        self.rawValue = value
        self.order = order
    }

    // an array of all of the Suit values
    static var allValues: [Suit]{
        var values = [Suit]()

        let mirror = Mirror(reflecting: SuitType.instance)
        for (_, v) in mirror.children{
            guard let suit = v as? Suit else{continue}
            values.append(suit)
        }

        return values
    }

    // allows for using Suit(rawValue:"?"), like a normal enum
    public init?(rawValue: String){
        // get the Suit from allValuesDictionary in SuitType, or return nil if that raw value doesn't exist
        guard let suit = SuitType.allValuesDictionary[rawValue] else{return nil}
        // initialize a new Suit with the same properties as that with the same raw value
        self.init(suit.rawValue, order: suit.order)
    }
}

你现在可以做像

1
let allSuits: [Suit] = Suit.allValues

1
2
3
for suit in Suit.allValues{
   print("The suit \(suit.rawValue) has the order \(suit.order)")
}

但是,要买单程票,你还是需要使用SuitType.instance.spadesSuitType.instance.hearts。为了让这更直观一点,您可以在Suit中添加一些代码,使您可以使用Suit.type.*而不是SuitType.instance.*

1
2
3
4
5
6
7
public struct Suit: RawRepresentable, Hashable{
   // ...your code...

   static var type = SuitType.instance

   // ...more of your code...
}

你现在可以用Suit.type.diamonds代替SuitType.instance.diamonds,或者用Suit.type.clubs代替SuitType.instance.clubs


Swift 5解决方案:Swift 5中的解决方案非常简单:

1
2
3
4
5
6
7
8
9
10
11
12
enum Suit: String, CaseIterable {
    case spades ="?"
    case hearts ="?"
    case diamonds ="?"
    case clubs ="?"
}

// then access the cases like this:

for suitKey in LocalizationKey.allCases {
    print(suitKey)
}

有时,您可以使用在整个软件开发生命周期中更改的底层原始整数类型来处理枚举类型。下面是一个很好的例子:

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
public class MyClassThatLoadsTexturesEtc
{
    //...

    // Colors used for gems and sectors.
    public enum Color: Int
    {
        // Colors arranged in order of the spectrum.
        case First = 0
        case Red, Orange, Yellow, Green, Blue, Purple, Pink
        // --> Add more colors here, between the first and last markers.
        case Last
    }

    //...

    public func preloadGems()
    {
        // Preload all gems.
        for i in (Color.First.toRaw() + 1) ..< (Color.Last.toRaw())
        {
            let color = Color.fromRaw(i)!
            loadColoredTextures(forKey: color)
        }
    }

    //...
}


我的解决方案是声明一个具有所有枚举可能性的数组,以便for可以遍历所有枚举可能性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//Function inside struct Card
static func generateFullDeck() -> [Card] {
    let allRanks = [Rank.Ace, Rank.Two, Rank.Three, Rank.Four, Rank.Five, Rank.Six, Rank.Seven, Rank.Eight, Rank.Nine, Rank.Ten, Rank.Jack, Rank.Queen, Rank.King]
    let allSuits = [Suit.Hearts, Suit.Diamonds, Suit.Clubs, Suit.Spades]
    var myFullDeck: [Card] = []

    for myRank in allRanks {
        for mySuit in allSuits {
            myFullDeck.append(Card(rank: myRank, suit: mySuit))
        }
    }
    return myFullDeck
}

//actual use:
let aFullDeck = Card.generateFullDeck()    //Generate the desired full deck

var allDesc: [String] = []
for aCard in aFullDeck {
    println(aCard.simpleDescription())    //You'll see all the results in playground
}

我添加了函数count(),并迭代这些值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public enum MetricType : Int {
case mvps = 0
case allNBA = 1
case championshipRings = 2
case finalAppearances = 3
case gamesPlayed = 4
case ppg = 5

static func count() -> Int {
    return (ppg.rawValue) + 1
}

static var allValues: [MetricType] {
    var array: [MetricType] = Array()
    var item : MetricType = MetricType.mvps
    while item.rawValue < MetricType.count() {
        array.append(item)
        item = MetricType(rawValue: (item.rawValue + 1))!
    }
    return array
}

}


在swift上,可以访问enum类型,如EnumType.Case类型:

let tableView = UITableView(frame: self.view.bounds, style: UITableViewStyle.Plain)

大多数情况下,只有当您有一些选择要处理,并且确切知道要对每种类型做什么时,才使用enum类型。

在使用enum类型时,使用for-in结构没有多大意义。

您可以这样做,例如:

1
2
3
4
5
6
7
8
9
func sumNumbers(numbers : Int...) -> Int {
    var sum = 0

    for number in numbers{
        sum += number
    }

    return sum
}


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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
enum Rank: Int {
    case Ace = 1
    case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
    case Jack, Queen, King

    func simpleDescription() -> String {
        switch self {
        case .Ace: return"ace"
        case .Jack: return"jack"
        case .Queen: return"queen"
        case .King: return"king"
        default: return String(self.toRaw())
        }
    }
}

enum Suit: Int {
    case Spades = 1
    case Hearts, Diamonds, Clubs

    func simpleDescription() -> String {
        switch self {
        case .Spades: return"spades"
        case .Hearts: return"hearts"
        case .Diamonds: return"diamonds"
        case .Clubs: return"clubs"
        }
    }

    func color() -> String {
        switch self {
        case .Spades, .Clubs: return"black"
        case .Hearts, .Diamonds: return"red"
        }
    }
}

struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return"The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }

    static func createPokers() -> Card[] {
        let ranks = Array(Rank.Ace.toRaw()...Rank.King.toRaw())
        let suits = Array(Suit.Spades.toRaw()...Suit.Clubs.toRaw())
        let cards = suits.reduce(Card[]()) { (tempCards, suit) in
            tempCards + ranks.map { rank in
                Card(rank: Rank.fromRaw(rank)!, suit: Suit.fromRaw(suit)!)
            }
        }
        return cards
    }
}

这里有一个不那么神秘的例子,如果您仍然想对RankSuit使用枚举。如果您想使用for-in loop对每个数组进行迭代,只需将它们收集到一个数组中。

标准52卡片组示例:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
enum Rank: Int {
    case Ace = 1, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King
    func name() -> String {
        switch self {
        case .Ace:
            return"ace"
        case .Jack:
            return"jack"
        case .Queen:
            return"queen"
        case .King:
            return"king"
        default:
            return String(self.toRaw())
        }
    }
}

enum Suit: Int {
    case Diamonds = 1, Clubs, Hearts, Spades
    func name() -> String {
        switch self {
        case .Diamonds:
            return"diamonds"
        case .Clubs:
            return"clubs"
        case .Hearts:
            return"hearts"
        case .Spades:
            return"spades"
        default:
            return"NOT A VALID SUIT"
        }
    }
}

let Ranks = [
    Rank.Ace,
    Rank.Two,
    Rank.Three,
    Rank.Four,
    Rank.Five,
    Rank.Six,
    Rank.Seven,
    Rank.Eight,
    Rank.Nine,
    Rank.Ten,
    Rank.Jack,
    Rank.Queen,
    Rank.King
]

let Suits = [
    Suit.Diamonds,
    Suit.Clubs,
    Suit.Hearts,
    Suit.Spades
]


class Card {
    var rank: Rank
    var suit: Suit

    init(rank: Rank, suit: Suit) {
        self.rank = rank
        self.suit = suit
    }
}

class Deck {
    var cards = Card[]()

    init() {
        for rank in Ranks {
            for suit in Suits {
                cards.append(Card(rank: rank, suit: suit))
            }
        }
    }
}

var myDeck = Deck()
myDeck.cards.count  // => 52