NSFastEnumeration в Swift

Я пытаюсь преобразовать проект Objective-C в swift, но я не могу найти, как использовать NSFastEnumeration для объекта класса, который соответствует NSFastEnumeration.

Вот код в ObjC:

//  get the decode results
id<NSFastEnumeration> results = [info objectForKey: ZBarReaderControllerResults];

ZBarSymbol *symbol = nil;
for(symbol in results)
    // just grab the first barcode
    break;

До сих пор я пытался найти, как это сделать, но это не похоже на работу, вот быстрый код:

var results: ZBarSymbolSet = infoDictionary?.objectForKey(ZBarReaderControllerResults) as ZBarSymbolSet

    var symbol : ZBarSymbol? = nil;

    for symbol in results
    {    //just grab first barcode
        break;
    }

ошибка возникает для условия - "ZBarSymbolSet" не имеет члена с именем "Generator"

Что я делаю неправильно?

Вот скриншот enter image description here

Ответы

Ответ 1

Спустя некоторое время, прокручивая быстрые файлы фреймворков, я наконец нашел этот класс, который называется NSFastGenerator. NSSet и друзья, похоже, используют один и тот же Generator.

Для ZBarSymbolSet, как вы можете расширить его для поддержки циклов for-in:

extension ZBarSymbolSet: SequenceType {
    public func generate() -> NSFastGenerator {
        return NSFastGenerator(self)
    }
}

Обновление: Похоже, что расширители протокола Swift 2.0 исправили это для нас!

Ответ 2

Ваш определенный класс ZBarSymbolSet должен реализовать интерфейс Swift SequenceType, чтобы он мог использоваться в синтаксисе for <identifier> in <sequence>. Интерфейс SequenceType

protocol SequenceType : _Sequence_Type {
    typealias Generator : GeneratorType
    func generate() -> Generator
}

и, таким образом, вы видите упоминание Generator, как указано в сообщении об ошибке.

Также в синтаксисе:

for <identifier> in <sequence> {
  <statements>
}

<identifer> доступен только для <statements>. Таким образом, ваше второе использование symbol в if будет за пределами области видимости и ошибки. Один правильный идиома:

var symbolFound : ZBarSymbol?

for symbol in result {
  symbolFound = symbol
  break
}

if symbolFound ...

Если курс, но время ZBarSymbolSet реализует SequenceType, он также реализует CollectionType с subscript, и, таким образом, весь код "найти первый элемент" будет var symbol = result[0]

Ответ 3

Step1: 
extension ZBarSymbolSet: SequenceType {
    public func generate() -> NSFastGenerator {
        return NSFastGenerator(self)
    }
}

Step2:
var results: NSFastEnumeration = info.objectForKey(ZBarReaderControllerResults) as NSFastEnumeration

    var symbolFound : ZBarSymbol?

    for symbol in results as ZBarSymbolSet {
        symbolFound = symbol as? ZBarSymbol
        break
    }
    resultString = NSString(string: symbolFound!.data)

Ответ 4

Вот ответ Джон Эстропией для Swift 3:

extension ZBarSymbolSet: Sequence {

    public typealias Iterator = NSFastEnumerationIterator

    public func makeIterator() -> NSFastEnumerationIterator {
        return NSFastEnumerationIterator(self)
    }

}

Тогда цикл for-in будет выглядеть следующим образом:

for element in results {
    let symbol = element as! ZBarSymbol
    // ...
}

Этот ответ можно было бы улучшить, приняв IteratorProtocol, чтобы вы могли указать связанный с элементом тип как ZBarSymbol. Я еще не понял, как это сделать.

Ответ 5

Кроме того, если вы знаете, что все объекты в ZBarSymbolSet являются объектами ZBarSymbol (так как ObjC не применяет все объекты, являющиеся объектами ZBarSymbol), вы можете сделать следующее:

extension ZBarSymbolSet {

    public struct ZBarSymbolSetIterator {
        public typealias Element = ZBarSymbol
        private let enumerator: NSFastEnumerationIterator

        init(_ symbols: ZBarSymbolSet) {
            self.enumerator = NSFastEnumerationIterator(symbols)
        }

        public mutating func next() -> ZBarSymbol {
            if let object = self.enumerator.next() {
                return object as? ZBarSymbol
            }
            else { return nil }
        }
    }

    public func makeIterator() -> ZBarSymbolSetIterator {
        return ZBarSymbolSetIterator(self)
    }
}

Теперь ваш цикл for будет выглядеть следующим образом:

for element in results {
    // element is a ZBarSymbol
}