We often use the for-in loop in the Swift programming language (also known as For-each in programming language terminology). Also, from the Swift 2.2 release, the For loop will only support the For-in form, not for the For i = 0; I < n; I+=1 {} This form, in order to use this form, only use while or repeat-while instead, or find a way to for-in.
In Swift, the standard library has defined many types that can directly support the for-in loop form, such as range, Array, Set, dictionary, and so on. So can we define a class or struct ourselves to support this iterative form of for-in? Of course! We need to take two steps to achieve this goal.
In the first step, we need to implement the Sequencetype protocol for classes or structs that use for-in loops. Sequencetype contains many container-related interface methods, but if we simply implement the for-in loop, we only need to implement its public func generate ()-Self.generator interface method. Self can only be used in the definition of protocol, but it is not possible to use self because self is a reference to an object and the protocol is not an object, so the Swift programming language introduces self (note that S is uppercase) to refer to the types defined in this agreement. The Generate method is used to generate a list of each element of the desired iteration. In addition, the ontology of generator is Generatortype, which is also a protocol that represents each element object of the required iteration, so we're going to do the second step.
The second step is to implement the Generatortype protocol. This protocol is relatively simple, just a public mutating func next ()-Self.element interface method. This element can be used to specify the type of each element.
Let's take a look at an example code:
//
// ViewController.swift
// SwiftTest
//
// Created by Zenny Chen on 16/4/1.
// Copyright © 2016 GreenGames Studio. All rights reserved.
//
import Cocoa
class MyIterContainer <T>: SequenceType {
// The container itself contains an array object mElemArray
private var mElemArray: [T]?
init () {
mElemArray = [T] ()
}
init (elems: [T]) {
mElemArray = elems;
}
func generate ()-> MyIterGenerator <T> {
// here returns a GeneratorType object
return MyIterGenerator (elems: mElemArray!)
}
}
class MyIterGenerator <T>: GeneratorType {
private var mCurrentIndex: Int = 0
private var mElemArray: [T]?
init (elems: [T]) {
mElemArray = elems
}
func next ()-> T? {
guard let list = mElemArray else {return nil}
if mCurrentIndex <list.count {
let element = list [mCurrentIndex]
mCurrentIndex + = 1
return element
}
else {
return nil
}
}
}
class ViewController: NSViewController {
override func viewDidLoad () {
super.viewDidLoad ()
// Do any additional setup after loading the view.
let container = MyIterContainer (elems: [1, 2, 3, 4])
var sum = 0
for i in container {
sum + = i
}
print ("sum = \ (sum)")
sum = 0
// The above for-in iteration is equivalent to the following code:
let generator = container.generate ()
var elem: Int? = nil
repeat {
elem = generator.next ()
if let value = elem {
sum + = value
}
}
while elem! = nil
print ("second sum = \ (sum)")
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
The example code above first defines a container class Myitercontainer<t>, and then defines the associated generator class Myitergenerator<t>, where generics are used to make later implementations more flexible. Then the method of using for-in is described in the Viewdidload method, and its internal implementation mechanism is described at the end.
How to implement a custom type of for-in loop in the Swift programming language (based on Swift 2.2)