添加“for in”支持以迭代Swift自定义类

时间:2014-06-07 16:23:45

标签: swift

我们知道,我们可以使用for..in循环来遍历ArraysDictionaries。但是,我想像我这样迭代我自己的CustomClass

for i in CustomClass {
    someFunction(i)
}

CustomClass必须支持哪些操作/协议?

6 个答案:

答案 0 :(得分:31)

假设你有一个班级"汽车"您希望能够使用for..in循环迭代:

let cars = Cars()

for car in cars {
    println(car.name)
}

最简单的方法是将AnyGenerator与以下类一起使用:

class Car {
    var name : String
    init(name : String) {
        self.name = name
    }
}

class Cars : SequenceType {

    var carList : [Car] = []

    func generate() -> AnyGenerator<Car> {
        // keep the index of the next car in the iteration
        var nextIndex = carList.count-1

        // Construct a AnyGenerator<Car> instance, passing a closure that returns the next car in the iteration
        return anyGenerator {
            if (nextIndex < 0) {
                return nil
            }
            return self.carList[nextIndex--]
        }
    }
}

要尝试一个完整的工作示例,请添加上面的两个类,然后尝试像这样使用它们,添加几个测试项:

    let cars = Cars()

    cars.carList.append(Car(name: "Honda"))
    cars.carList.append(Car(name: "Toyota"))

    for car in cars {
        println(car.name)
    }


简单来说就是这样。

更多信息:http://lillylabs.no/2014/09/30/make-iterable-swift-collection-type-sequencetype

答案 1 :(得分:12)

以上所有答案都有点棘手。如果你的类中有一个你想要迭代的数组(比如@Lee Whitney的答案),那么有一个更简单的方法来实现它。您有以下类CustomClass:

class CustomClass: SequenceType {
    let array: [String]

    init(array: [String]) {
        self.array = array
    }

    func generate() -> IndexingGenerator<[String]> {
        return array.generate()
    }
}

这很简单。经过测试,可以使用最新的Xcode版本(编写本文时为6.1)和iOS 8.1.2。但是,此代码在未来的版本中应该是稳定的。

P.S。使用泛型,您可以通过遵循此模式轻松地创建自己的Array副本,并且只实现您想要的方法。

答案 2 :(得分:4)

@Matt Gibson是对的。但是,我想添加更多信息以供将来参考。

来自Advanced Swift:

此代码:

for x in someSequence {
    ...
}

转换成这个:

var __g = someSequence.generate()
while let x = __g.next() {
    ...
}

因此,必须采用序列,它给出了类generate()next()。以下是这些协议:

protocol Generator {
    typealias Element
    mutating func next() -> Element?
}
protocol Sequence {
    typealias GeneratorType : Generator
    func generate() -> GeneratorType
}

答案 3 :(得分:2)

这将是SequenceType协议及其相关的Generator协议。

SequenceType协议表示该类必须实现generate(),它返回符合Generator协议的内容,这是执行实际工作的内容; Generator协议是具有最重要的next()方法的协议。

WWDC 2014 video&#34; Advanced Swift&#34;中实现它以允许for..in的示例(在泛型示例&#34;简单通用堆栈&#34;,从幻灯片183开始。)

有关for..in的实施协议的基本信息位于文档的Statements部分,其中简要概述了SequenceTypeGenerator

答案 4 :(得分:0)

  

注意 AnyGeneratorSequenceType是最新版本中不存在的旧类型。您现在需要实现Sequence协议。

有两种实现Sequence的方式。

  1. 只需实现Sequence方法即可同时符合IteratorProtocolnext()。这种方法最简单,并且标头中有一个示例。参见Sequence.swift。同时实施这两个协议可能是不现实的或无法满足您的需求。 (这可能会阻止同时重复两个不同的实例,依此类推)

  2. 符合Sequence并返回实现IteratorProtocol的对象。我认为这是现实世界中最常见的情况(情况变得有些复杂,而不是Hello Worlds)。 Sequence.swift

  3. 中也有一个示例

下面是方法2的示例。一个可迭代的自定义类(链接列表):

/// Linked List Node:
public class LinkedListNode <T> {

    public internal(set) var value: T

    public internal(set) var next: LinkedListNode<T>?

    internal init(_ value: T) {
        self.value = value
        self.next = nil
    }
}

/// Linked List with append method only.
public class LinkedList<T> {

    public internal(set) var first: LinkedListNode<T>? = nil

    public internal(set) var last: LinkedListNode<T>? = nil

    /// Appends a new node.
    public func append(_ value: T) {
        if first == nil {
            first = LinkedListNode(value)
            last = first
        } else {
            last.next = LinkedListNode(value)
            last = last.next
        }
    }
}

最后实现序列

/// Sequence protocol adoption. It allows `for ... in` and a bunch of other methods too.
extension LinkedList: Sequence {

    /// Iterator implementation
    public class Iterator<T>: IteratorProtocol {

        /// Maintain a ref to current element so next element can be reached
        var cur: LinkedListNode<T>?

        /// IteratorProtocol protocol requirement
        public func next() -> T? {
            let res = cur?.value
            cur = cur?.next
            return res
        }
    }

    /// Sequence protocol requirement
    public func makeIterator() -> Iterator<T> {
        let g = LinkedList.Iterator()
        g.cur = first
        return g
    }
}

用法:

let linkedList = LinkedList<Int>()
linkedList.append(3)
linkedList.append(6)
linkedList.append(9)
linkedList.append(12)
for element in linkedList {
    print(element)
}

let odds = linkedList.filter { return $0 % 2 == 0 }
print(odds)

答案 5 :(得分:-1)

接受的答案是正确的,直到最近才是解决这个问题的公认方法。但是,鉴于在Swift 2.0中引入了Protocol Extensions,而不是与SequenceType的一致性和实现func generate() -> GeneratorOf<Car>,现在有一个抽象基类来处理这个函数的实现,称为AnyGenerator<T>see Apple docs),因为GeneratorOf<T>已不存在。

这意味着你可以简单地为这个抽象基类创建子类,并且通过这样做,继承上述协议一致性的所有功能:

class Cars: AnyGenerator<Car> {

     private var carList = [Car]()
     private var currentIndex:Int

     ...

}

然后只需覆盖next()协议声明的GeneratorType方法(AnyGenerator<T>也符合),以便定义所需的迭代行为:

class Cars: AnyGenerator<Car> {

     private var carList = [Car]()
     private var currentIndex:Int

     override func next() -> Car? {

         if (currentIndex < self.carList.count) {
            currentIndex++
            return self.carList[currentIndex-1]
         } else {
            currentIndex = 0;
            return nil
         }
     }

}