enum Suit: String {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
}
For example, how can I do something like:
for suit in Suit {
// do something with suit
print(suit.rawValue)
}
Resulting example:
♠
♥
♦
♣
解决方案
I made a utility function iterateEnum() for iterating cases for arbitrary enum types.
Here is the example usage:
enum Suit:String {
case Spades = "♠"
case Hearts = "♥"
case Diamonds = "♦"
case Clubs = "♣"
}
for f in iterateEnum(Suit) {
println(f.rawValue)
}
outputs:
♠
♥
♦
♣
But, this is only for debug or test purpose: This relies on several undocumented current(Swift1.1) compiler behaviors. So, use it at your own risk :)
Here is the code:
func iterateEnum(_: T.Type) -> GeneratorOf {
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
}
}
The underlying idea is:
Memory representation of enum - excluding enums with associated types - is just a index of cases, when the count of the cases is 2...256, it's identical to UInt8, when 257...65536, it's UInt16 and so on. So, it can be unsafeBitcast from corresponding unsigned integer types.
.hashValue of enum values is the same as the index of the case.
.hashValue of enum values bitcasted from invalid index is 0
ADDED:
Revised for Swift2 and implemented casting ideas from @Kametrixom's answer
func iterateEnum(_: T.Type) -> AnyGenerator {
var i = 0
return anyGenerator {
let next = withUnsafePointer(&i) { UnsafePointer($0).memory }
return next.hashValue == i++ ? next : nil
}
}
ADDED:
Revised for Swift3
func iterateEnum(_: T.Type) -> AnyIterator {
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
}
}
ADDED:
Revised for Swift3.0.1
func iterateEnum(_: T.Type) -> AnyIterator {
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
}
}