Swift 열거 형에서 사례 수를 어떻게 확인할 수 있습니까?
( 모든 값을 수동으로 열거 하거나 가능한 경우 이전 " enum_count trick "을 사용하지 않기를 원합니다 .)
Swift 열거 형에서 사례 수를 어떻게 확인할 수 있습니까?
( 모든 값을 수동으로 열거 하거나 가능한 경우 이전 " enum_count trick "을 사용하지 않기를 원합니다 .)
답변:
Swift 4.2 (Xcode 10)부터 CaseIterable
프로토콜 준수를 선언 할 수 있으며 관련 값이없는 모든 열거에 작동합니다.
enum Stuff: CaseIterable {
case first
case second
case third
case forth
}
사례 수는 이제 간단히
print(Stuff.allCases.count) // 4
자세한 내용은
이에 대한 자세한 내용 의 블로그 게시물 이 있지만 열거 형의 원시 유형이 정수이면 다음과 같이 카운트를 추가 할 수 있습니다.
enum Reindeer: Int {
case Dasher, Dancer, Prancer, Vixen, Comet, Cupid, Donner, Blitzen
case Rudolph
static let count: Int = {
var max: Int = 0
while let _ = Reindeer(rawValue: max) { max += 1 }
return max
}()
}
case A=1, B=3
?
enum
갖는 것 외에 두 가지 가정 Int
이 있습니다. 기본 동작이지만 1 씩 증가합니다.
Xcode 10 업데이트
CaseIterable
열거 형 의 프로토콜을 채택하면 allCases
모든 열거 형 케이스를 포함 하는 정적 속성을 제공 합니다 Collection
. 그냥 사용count
속성을 하여 열거 형의 사례 수를 알 수 있습니다.
예를 들어 Martin의 답변을 참조하십시오 (그리고 내 대답보다는 찬성 투표하십시오)
경고 : 아래 방법은 더 이상 작동하지 않는 것 같습니다.
열거 형 수를 계산하는 일반적인 방법을 알지 못합니다. 그러나 hashValue
열거 형 사례 의 속성은 0부터 시작하여 사례가 선언 된 순서에 따라 결정되는 점진적 인 것으로 나타났습니다 . 따라서 마지막 열거 형의 해시에 1을 더한 경우는 사례 수에 해당합니다.
예를 들어이 열거 형을 사용하면 :
enum Test {
case ONE
case TWO
case THREE
case FOUR
static var count: Int { return Test.FOUR.hashValue + 1}
}
count
4를 반환합니다.
나는 그것이 규칙인지 또는 미래에 변경 될 것인지 말할 수 없으므로 자신의 책임하에 사용하십시오 :)
hashValues
이런 것들 에 의존해서는 안됩니다 . 우리가 아는 것은 임의의 고유 값이라는 것입니다. 컴파일러 구현 세부 사항에 따라 미래에 매우 쉽게 변경 될 수 있습니다. 그러나 전반적으로 빌트인 카운팅 기능의 부족은 혼란 스럽습니다.
case ONE = 0
, 당신은 그 때 교체 할 수 있습니다 hashValue
로 rawValue
.
static var count = 4
Swift의 미래 구현 운명에 운명을 남기지 않고 더 나은 것을 사용하는 것이 더 안전하고 안전합니다
Nate Cook이 게시 한 접근 방식에 따라 사례 수를 자동으로 수행하는 재사용 가능한 프로토콜을 정의합니다.
protocol CaseCountable {
static var caseCount: Int { get }
}
extension CaseCountable where Self: RawRepresentable, Self.RawValue == Int {
internal static var caseCount: Int {
var count = 0
while let _ = Self(rawValue: count) {
count += 1
}
return count
}
}
그런 다음이 프로토콜을 예를 들어 다음과 같이 재사용 할 수 있습니다.
enum Planet : Int, CaseCountable {
case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}
//..
print(Planet.caseCount)
count++
count+=1
++
static var caseCount: Int { get }
습니까? 왜 필요한가 static func
?
case A=1, B=3
?
0
간격이 없어야합니다.
이 답변에 표시된 것처럼 정적 allValues 배열을 만듭니다.
enum ProductCategory : String {
case Washers = "washers", Dryers = "dryers", Toasters = "toasters"
static let allValues = [Washers, Dryers, Toasters]
}
...
let count = ProductCategory.allValues.count
이것은 값을 열거하고 모든 Enum 유형에서 작동 할 때 유용합니다.
static let count = allValues.count
. 그런 다음 allValues
원하는 경우 개인을 만들 수 있습니다.
구현에 정수 열거 형을 사용하는 것에 대한 내용이없는 경우 열거 형 Count
의 멤버 수를 나타 내기 위해 추가 멤버 값을 추가 할 수 있습니다 ( 아래 예 참조).
enum TableViewSections : Int {
case Watchlist
case AddButton
case Count
}
이제 호출하여 열거 형의 멤버 수를 얻을 수 있습니다 TableViewSections.Count.rawValue
. 위의 예에서는 2를 반환합니다.
switch 문에서 열거 형을 처리 할 때 Count
예상치 못한 멤버 가 발생할 때 어설 션 오류를 발생시켜야 합니다.
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let currentSection: TableViewSections = TableViewSections.init(rawValue:section)!
switch(currentSection) {
case .Watchlist:
return watchlist.count
case .AddButton:
return 1
case .Count:
assert(false, "Invalid table view section!")
}
}
이러한 종류의 함수는 열거의 개수를 반환 할 수 있습니다.
스위프트 2 :
func enumCount<T: Hashable>(_: T.Type) -> Int {
var i = 1
while (withUnsafePointer(&i) { UnsafePointer<T>($0).memory }).hashValue != 0 {
i += 1
}
return i
}
스위프트 3 :
func enumCount<T: Hashable>(_: T.Type) -> Int {
var i = 1
while (withUnsafePointer(to: &i, {
return $0.withMemoryRebound(to: T.self, capacity: 1, { return $0.pointee })
}).hashValue != 0) {
i += 1
}
return i
}
enum
인 아니라 Hashable
동일한 유형.
인덱스가있는 문자열 열거 형
enum eEventTabType : String {
case Search = "SEARCH"
case Inbox = "INBOX"
case Accepted = "ACCEPTED"
case Saved = "SAVED"
case Declined = "DECLINED"
case Organized = "ORGANIZED"
static let allValues = [Search, Inbox, Accepted, Saved, Declined, Organized]
var index : Int {
return eEventTabType.allValues.indexOf(self)!
}
}
카운트 : eEventTabType.allValues.count
인덱스 : objeEventTabType.index
즐겨 :)
모두 안녕, 단위 테스트는 어때?
func testEnumCountIsEqualToNumberOfItemsInEnum() {
var max: Int = 0
while let _ = Test(rawValue: max) { max += 1 }
XCTAssert(max == Test.count)
}
이것은 Antonio의 솔루션과 결합되었습니다.
enum Test {
case one
case two
case three
case four
static var count: Int { return Test.four.hashValue + 1}
}
메인 코드를 당신에게 제공 O (1) 플러스 당신이 실패 테스트를받을 사람이 열거 케이스를 추가하는 경우 five
와의 구현을 업데이트하지 않습니다 count
.
이 함수는 문서화되지 않은 두 가지 현재 (Swift 1.1) enum
동작 에 의존합니다 .
enum
은 단지 인덱스입니다 case
. 사례 개수가 2 ~ 256 인 경우 UInt8
입니다.enum
였다에서 비트 캐스트 유효하지 않은 경우 지수의 hashValue
IS0
따라서 귀하의 책임하에 사용하십시오 :)
func enumCaseCount<T:Hashable>(t:T.Type) -> Int {
switch sizeof(t) {
case 0:
return 1
case 1:
for i in 2..<256 {
if unsafeBitCast(UInt8(i), t).hashValue == 0 {
return i
}
}
return 256
case 2:
for i in 257..<65536 {
if unsafeBitCast(UInt16(i), t).hashValue == 0 {
return i
}
}
return 65536
default:
fatalError("too many")
}
}
용법:
enum Foo:String {
case C000 = "foo"
case C001 = "bar"
case C002 = "baz"
}
enumCaseCount(Foo) // -> 3
원시 값이 정수 인 모든 열거 형을 제공하는 간단한 확장을 작성했습니다 count
.
extension RawRepresentable where RawValue: IntegerType {
static var count: Int {
var i: RawValue = 0
while let _ = Self(rawValue: i) {
i = i.successor()
}
return Int(i.toIntMax())
}
}
불행히도 count
속성이 OptionSetType
제대로 작동하지 않는 곳을 제공 하므로 CaseCountable
계산하려는 열거 형에 대해 프로토콜을 명시 적으로 준수 해야하는 다른 버전이 있습니다.
protocol CaseCountable: RawRepresentable {}
extension CaseCountable where RawValue: IntegerType {
static var count: Int {
var i: RawValue = 0
while let _ = Self(rawValue: i) {
i = i.successor()
}
return Int(i.toIntMax())
}
}
Tom Pelaia가 게시 한 접근 방식과 매우 유사하지만 모든 정수 유형에서 작동합니다.
enum EnumNameType: Int {
case first
case second
case third
static var count: Int { return EnumNameType.third.rawValue + 1 }
}
print(EnumNameType.count) //3
또는
enum EnumNameType: Int {
case first
case second
case third
case count
}
print(EnumNameType.count.rawValue) //3
* Swift 4.2 (Xcode 10)에서는 다음을 사용할 수 있습니다.
enum EnumNameType: CaseIterable {
case first
case second
case third
}
print(EnumNameType.allCases.count) //3
내 유스 케이스의 경우 여러 사람이 열거에 키를 추가 할 수있는 코드베이스에서 allKeys 속성에서 이러한 케이스를 모두 사용할 수 있어야하므로 열거의 키에 대해 allKeys의 유효성을 검사하는 것이 중요합니다. 이것은 누군가가 자신의 키를 모든 키 목록에 추가하는 것을 잊는 것을 피하기위한 것입니다.열거의 키 수에 대해 allKeys 배열 (처음 삭제를 피하기 위해 세트로 작성된) 수를 일치 시키면 모두 존재합니다.
위의 답변 중 일부는 Swift 2에서이를 달성하는 방법을 보여 주지만 Swift 3 에서는 작동하지 않습니다 . 다음은 Swift 3 형식 버전입니다.
static func enumCount<T: Hashable>(_ t: T.Type) -> Int {
var i = 1
while (withUnsafePointer(to: &i) {
$0.withMemoryRebound(to:t.self, capacity:1) { $0.pointee.hashValue != 0 }
}) {
i += 1
}
return i
}
static var allKeys: [YourEnumTypeHere] {
var enumSize = enumCount(YourEnumTypeHere.self)
let keys: Set<YourEnumTypeHere> = [.all, .your, .cases, .here]
guard keys.count == enumSize else {
fatalError("Missmatch between allKeys(\(keys.count)) and actual keys(\(enumSize)) in enum.")
}
return Array(keys)
}
사용 사례에 따라 각 요청에서 allKeys를 사용하는 오버 헤드를 피하기 위해 개발에서 테스트를 실행하는 것이 좋습니다.
왜 그렇게 복잡하게 만드나요? Int enum의 SIMPLEST 카운터는 다음을 추가하는 것입니다.
case Count
결국. 그리고 ... 비올라-이제 카운트가 있습니다-빠르고 간단합니다
0
해야하며 시퀀스에 간격이 없습니다.
타입 열거 형으로 작업 하는 Swift 3 버전 Int
:
protocol CaseCountable: RawRepresentable {}
extension CaseCountable where RawValue == Int {
static var count: RawValue {
var i: RawValue = 0
while let _ = Self(rawValue: i) { i += 1 }
return i
}
}
크레딧 : bzz와 Nate Cook의 답변을 기반으로합니다.
Generic IntegerType
(Swift 3에서로 이름이 바뀐 Integer
)은 많은 기능이 부족한 조각난 제네릭 형식이므로 지원되지 않습니다. successor
더 이상 Swift 3에서는 사용할 수 없습니다.
Code Commander에서 Nate Cooks의 답변에 대한 주석은 여전히 유효합니다.
값을 하드 코딩 할 필요가 없기 때문에 좋지만 호출 될 때마다 모든 열거 형 값을 인스턴스화합니다. 그것은 O (1) 대신 O (n)입니다.
내가 아는 한 일반 유형에서 지원되지 않는 정적 저장 속성으로 인해 프로토콜 확장으로 사용하고 (Nate Cook처럼 각 열거 형에서 구현하지 않은 경우) 현재 해결 방법이 없습니다.
어쨌든, 작은 열거 형의 경우 이것은 문제가되지 않습니다. 일반적인 사용 사례가 될 것이라고 section.count
위해 UITableViews
이미 Zorayr 언급한다.
Matthieu Riegler의 답변을 확장하여 이것은 제네릭을 사용할 필요가없는 Swift 3 용 솔루션 이며 다음과 함께 열거 형을 사용하여 쉽게 호출 할 수 있습니다 EnumType.elementsCount
.
extension RawRepresentable where Self: Hashable {
// Returns the number of elements in a RawRepresentable data structure
static var elementsCount: Int {
var i = 1
while (withUnsafePointer(to: &i, {
return $0.withMemoryRebound(to: self, capacity: 1, { return
$0.pointee })
}).hashValue != 0) {
i += 1
}
return i
}
프로토콜 (EnumIntArray)과 전역 유틸리티 함수 (enumIntArray)를 만들어서 모든 열거 형에 "All"변수를 쉽게 추가 할 수있게함으로써 (swift 1.2 사용)이 문제를 직접 해결했습니다. "all"변수는 열거 형에있는 모든 요소의 배열을 포함하므로 개수에 all.count를 사용할 수 있습니다.
Int 유형의 원시 값을 사용하는 열거 형에서만 작동하지만 다른 유형에 대한 영감을 줄 수 있습니다.
또한 위에서 읽은 다른 곳에서 읽은 "번호 매기기 간격"및 "과도한 반복 시간"문제도 해결합니다.
EnumIntArray 프로토콜을 열거 형에 추가 한 다음 enumIntArray 함수를 호출하여 "all"정적 변수를 정의하고 첫 번째 요소 (번호 매기기에 공백이있는 경우 마지막)를 제공하는 것이 좋습니다.
정적 변수는 한 번만 초기화되므로 모든 원시 값을 통과하는 오버 헤드는 프로그램에 한 번만 적용됩니다.
예 (간격없이) :
enum Animals:Int, EnumIntArray
{
case Cat=1, Dog, Rabbit, Chicken, Cow
static var all = enumIntArray(Animals.Cat)
}
예 (갭이 있음) :
enum Animals:Int, EnumIntArray
{
case Cat = 1, Dog,
case Rabbit = 10, Chicken, Cow
static var all = enumIntArray(Animals.Cat, Animals.Cow)
}
이를 구현하는 코드는 다음과 같습니다.
protocol EnumIntArray
{
init?(rawValue:Int)
var rawValue:Int { get }
}
func enumIntArray<T:EnumIntArray>(firstValue:T, _ lastValue:T? = nil) -> [T]
{
var result:[T] = []
var rawValue = firstValue.rawValue
while true
{
if let enumValue = T(rawValue:rawValue++)
{ result.append(enumValue) }
else if lastValue == nil
{ break }
if lastValue != nil
&& rawValue > lastValue!.rawValue
{ break }
}
return result
}
다음 방법은 CoreKit 에서 왔으며 다른 사람들이 제안한 답변과 비슷합니다. 이것은 Swift 4에서 작동합니다.
public protocol EnumCollection: Hashable {
static func cases() -> AnySequence<Self>
static var allValues: [Self] { get }
}
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
}
}
}
public static var allValues: [Self] {
return Array(self.cases())
}
}
enum Weekdays: String, EnumCollection {
case sunday, monday, tuesday, wednesday, thursday, friday, saturday
}
그럼 당신은 그냥 전화해야합니다 Weekdays.allValues.count
.
struct HashableSequence<T: Hashable>: SequenceType {
func generate() -> AnyGenerator<T> {
var i = 0
return AnyGenerator {
let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }
if next.hashValue == i {
i += 1
return next
}
return nil
}
}
}
extension Hashable {
static func enumCases() -> Array<Self> {
return Array(HashableSequence())
}
static var enumCount: Int {
return enumCases().enumCount
}
}
enum E {
case A
case B
case C
}
E.enumCases() // [A, B, C]
E.enumCount // 3
열거 형 이외의 유형에주의하십시오. 몇 가지 해결 방법은 다음과 같습니다.
struct HashableSequence<T: Hashable>: SequenceType {
func generate() -> AnyGenerator<T> {
var i = 0
return AnyGenerator {
guard sizeof(T) == 1 else {
return nil
}
let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }
if next.hashValue == i {
i += 1
return next
}
return nil
}
}
}
extension Hashable {
static func enumCases() -> Array<Self> {
return Array(HashableSequence())
}
static var enumCount: Int {
return enumCases().count
}
}
enum E {
case A
case B
case C
}
Bool.enumCases() // [false, true]
Bool.enumCount // 2
String.enumCases() // []
String.enumCount // 0
Int.enumCases() // []
Int.enumCount // 0
E.enumCases() // [A, B, C]
E.enumCount // 4
열거의 마지막 값에 1을 더한 정적 상수를 사용할 수 있습니다.
enum Color : Int {
case Red, Orange, Yellow, Green, Cyan, Blue, Purple
static let count: Int = Color.Purple.rawValue + 1
func toUIColor() -> UIColor{
switch self {
case .Red:
return UIColor.redColor()
case .Orange:
return UIColor.orangeColor()
case .Yellow:
return UIColor.yellowColor()
case .Green:
return UIColor.greenColor()
case .Cyan:
return UIColor.cyanColor()
case .Blue:
return UIColor.blueColor()
case .Purple:
return UIColor.redColor()
}
}
}
이것은 작지만 더 나은 O (1) 솔루션은 다음과 같습니다 ( 열거가 x 등에서 시작하는 경우 에만Int
).
enum Test : Int {
case ONE = 1
case TWO
case THREE
case FOUR // if you later need to add additional enums add above COUNT so COUNT is always the last enum value
case COUNT
static var count: Int { return Test.COUNT.rawValue } // note if your enum starts at 0, some other number, etc. you'll need to add on to the raw value the differential
}
현재 선택한 답변은 여전히 모든 열거 형에 가장 적합한 답변이라고 생각 Int
합니다. 작업하지 않는 한이 솔루션을 권장합니다.
guard
에 대해이야 COUNT
및 유형의 표현의 우려를 해결하기 위해 오류, false를 반환 등을 발생합니다.