2016-12-27 2 views
5

У меня есть простой перечисление, которое я хотел бы перебрать. Для этой цели я принял Sequence и IteratorProtocol, как показано в приведенном ниже коде. Кстати, это может копировать/вставить в Playground в Xcode 8.Итерация через Enum в Swift 3.0

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) 
} 

Но для в цикле генерирует сообщение об ошибке, «Тип„Sections.Type“не соответствует протоколу„Sequence“» , Сопротивление протокола в моем расширении; так, что не так с этим кодом?

Я знаю, что есть другие способы сделать это, но я хотел бы понять, что не так с этим подходом.

Спасибо.

+0

Есть целый ряд ответов, которые решают проблему здесь (речь идет о типе струнных, но большинство ответов будет работать с любым типом) : http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type – kevin

ответ

5

Вы можете перебрать значение такого типа, который соответствует протоколу Sequence . Поэтому

for section in Sections.Section0 { 
    print(section) 
} 

будет скомпилировать и дать ожидаемый результат. Но, конечно, это не то, что вы хотите, потому что выбор значения произволен, и значение само по себе не нужно в последовательности.

Насколько я знаю, нет никакого способа, чтобы перебрать сам тип, так что

for section in Sections { 
    print(section) 
} 

компилирует. Это потребует, чтобы «метатип» 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) 
} 
+0

Существует предложение SE по существу «метатипа», который вы предлагаете; см. https://gist.github.com/andyyhope/2fc5b6bee8ee1346f688 Надеюсь, он принят, поскольку он, как представляется, является общей полезной функцией, которая позволит избежать всех видов пользовательских решений. Пока я буду придерживаться итераций по типу. Благодаря! – Phantom59

+0

Другую ссылку на ее статус можно найти здесь: https://github.com/apple/swift-evolution/pull/114 – Kdawgwilk

+0

Пожалуйста, также проверьте [это] (http://stackoverflow.com/a/28341290/4041795) answer .., который показывает, как создать функцию iterateEnum() для многих версий Swift. – Montmons

6

Обратите внимание, что 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 
} 

вы удаляете протокол и типы:

enum Fruits: Int, EnumSequence { 
    typealias T = Fruits 
    case apple, orange, pear 
} 

Fruits.all().forEach({ print($0) }) // apple orange pear 
1

Это выглядит намного проще:

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) 
    } 
} 
0

Iterated на указанных решениях см ниже протокола, который может быть реализован путем перечислений, чтобы добавить последовательность allValues, но и позволить возможность конвертировать и из строкового значения.

Очень удобно для String-подобных перечислений, которые должны поддерживать объектив 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!") 
    } 
} 
0

Если перечисление является 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() ..

Смежные вопросы