我们知道,我们可以使用for..in
循环来遍历Arrays
或Dictionaries
。但是,我想像我这样迭代我自己的CustomClass
:
for i in CustomClass {
someFunction(i)
}
CustomClass
必须支持哪些操作/协议?
答案 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部分,其中简要概述了SequenceType
和Generator
答案 4 :(得分:0)
注意
AnyGenerator
和SequenceType
是最新版本中不存在的旧类型。您现在需要实现Sequence
协议。
有两种实现Sequence
的方式。
只需实现Sequence
方法即可同时符合IteratorProtocol
,next()
。这种方法最简单,并且标头中有一个示例。参见Sequence.swift。同时实施这两个协议可能是不现实的或无法满足您的需求。 (这可能会阻止同时重复两个不同的实例,依此类推)
符合Sequence
并返回实现IteratorProtocol
的对象。我认为这是现实世界中最常见的情况(情况变得有些复杂,而不是Hello Worlds)。 Sequence.swift
下面是方法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
}
}
}