„Iterator“ ist eines der Entwurfsmuster, die Programmierer am häufigsten nicht bemerken, da seine Implementierung in der Regel direkt in die Standardwerkzeuge der Programmiersprache integriert ist. Dies ist jedoch auch eines der Verhaltensmuster, die im 
Buch „Gang of Four“, „GoF“, „Entwurfsmuster: Elemente wiederverwendbarer objektorientierter Software“ beschrieben und verstanden werden Sein Gerät tut nie weh und manchmal kann es sogar bei etwas helfen.
Ein „Iterator“ ist eine Methode für den sequentiellen Zugriff auf alle Elemente eines zusammengesetzten Objekts (insbesondere auf Containertypen wie ein Array oder eine Menge).Standard-Sprachwerkzeuge
Erstellen Sie eine Art 
Array :
let numbersArray = [0, 1, 2] 
... und dann in einem 
Zyklus durchgehen:
 for number in numbersArray { print(number) } 
... scheint eine sehr natürliche Handlung zu sein, insbesondere für moderne Programmiersprachen wie 
Swift . Hinter den Kulissen dieser einfachen Aktion befindet sich jedoch Code, der die Prinzipien des Iteratormusters implementiert.
In "Swift" muss der Variablentyp 
das Sequence implementieren, um eine Variable mit 
for -cycles "iterieren" zu können. Für dieses Protokoll muss dem Typ unter anderem ein 
Iterator vom Typ Typ 
associatedtype , der wiederum die Anforderungen des 
IteratorProtocol Protokolls implementieren muss, sowie die 
Factory-Methode makeIterator() , die einen bestimmten "Iterator" für diesen Typ zurückgibt:
 protocol Sequence { associatedtype Iterator : IteratorProtocol func makeIterator() -> Self.Iterator  
Das 
IteratorProtocol Protokoll enthält wiederum nur eine Methode - 
next() , die das nächste Element in der Sequenz zurückgibt:
 protocol IteratorProtocol { associatedtype Element mutating func next() -> Self.Element? } 
Es klingt wie "viel komplizierter Code", ist es aber eigentlich nicht. Unten werden wir das sehen.
Der 
Array Typ implementiert das 
Sequence (allerdings nicht direkt, sondern über die 
Protokollvererbungskette : Die 
MutableCollection erbt die 
Collection Anforderungen und letztere die 
Sequence Anforderungen), sodass insbesondere seine Instanzen mit 
for -cycles „iteriert“ werden können.
Benutzerdefinierte Typen
Was muss getan werden, um Ihren eigenen Typ iterieren zu können? Wie so oft ist es am einfachsten, ein Beispiel zu zeigen.
Angenommen, es gibt einen Typ, der ein Bücherregal darstellt, in dem bestimmte Instanzen einer Klasse gespeichert sind, die wiederum ein Buch darstellen:
 struct Book { let author: String let title: String } struct Shelf { var books: [Book] } 
Um eine Instanz der 
Shelf Klasse "iterieren" zu können, muss diese Klasse die Anforderungen des 
Sequence erfüllen. In diesem Beispiel reicht es aus, nur die Methode 
makeIterator() zu implementieren, zumal die anderen Protokollanforderungen 
Standardimplementierungen haben. Diese Methode sollte eine Instanz eines Typs zurückgeben, der das 
IteratorProtocol Protokoll implementiert. Glücklicherweise ist dies im Fall von Swift sehr wenig sehr einfacher Code:
 struct ShelfIterator: IteratorProtocol { private var books: [Book] init(books: [Book]) { self.books = books } mutating func next() -> Book? {  
Die 
next() -Methode vom Typ 
ShelfIterator als 
mutating deklariert, da die 
ShelfIterator den 
ShelfIterator speichern muss, der der aktuellen Iteration entspricht:
 mutating func next() -> Book? { defer { if !books.isEmpty { books.removeFirst() } } return books.first } 
Diese Implementierungsoption gibt immer das erste Element in der Sequenz zurück oder 
nil wenn die Sequenz leer ist. Der 
defer mit dem Code zum Ändern der iterierten Auflistung "umbrochen", wodurch das Element des letzten Iterationsschritts unmittelbar nach der Rückkehr der Methode entfernt wird.
Anwendungsbeispiel:
 let book1 = Book(author: ". ", title: "") let book2 = Book(author: ". ", title: " ") let book3 = Book(author: ". ", title: " ") let shelf = Shelf(books: [book1, book2, book3]) for book in shelf { print("\(book.author) – \(book.title)") }  
Weil Alle verwendeten Typen (einschließlich des 
Array zugrunde liegenden 
Shelf ) basieren auf der 
Semantik von Werten (im Gegensatz zu Referenzen) . Sie müssen sich keine Sorgen machen, dass der Wert der ursprünglichen Variablen während der Iteration geändert wird. Bei der Behandlung von Typen, die auf der Link-Semantik basieren, sollte dieser Punkt berücksichtigt und beim Erstellen eigener Iteratoren berücksichtigt werden.
Klassische Funktionalität
Der im Buch „Gangs of Four“ beschriebene klassische „Iterator“ kann neben der Rückgabe des nächsten Elements der iterierbaren Sequenz auch jederzeit das aktuelle Element im Iterationsprozess zurückgeben, wobei das erste Element der iterierbaren Sequenz und der Wert des „Flags“ angeben, ob noch vorhanden sind Elemente in einer iterierten Sequenz relativ zum aktuellen Iterationsschritt.
Natürlich wäre es einfach, ein Protokoll zu deklarieren, wodurch die Funktionen des Standard- 
IteratorProtocol :
 protocol ClassicIteratorProtocol: IteratorProtocol { var currentItem: Element? { get } var first: Element? { get } var isDone: Bool { get } } 
Das erste und das aktuelle Element werden seitdem optional zurückgegeben Die Quellsequenz ist möglicherweise leer.
Einfache Implementierungsoption:
 struct ShelfIterator: ClassicIteratorProtocol { var currentItem: Book? = nil var first: Book? var isDone: Bool = false private var books: [Book] init(books: [Book]) { self.books = books first = books.first currentItem = books.first } mutating func next() -> Book? { currentItem = books.first books.removeFirst() isDone = books.isEmpty return books.first } } 
In der ursprünglichen Beschreibung des Musters ändert die 
next() -Methode den internen Status des Iterators, um zum nächsten Element zu wechseln, und ist vom Typ 
Void . Das aktuelle Element wird von der 
currentElement() -Methode zurückgegeben. Im 
IteratorProtocol Protokoll sind diese beiden Funktionen so, als ob sie zu einer kombiniert würden.
Die Notwendigkeit der 
first() -Methode ist ebenfalls zweifelhaft, weil Der Iterator ändert die ursprüngliche Sequenz nicht und wir haben immer die Möglichkeit, auf sein erstes Element zuzugreifen (falls vorhanden, natürlich).
Und da die 
next() -Methode nach Abschluss der Iteration 
nil zurückgibt, wird auch die 
isDone() -Methode unbrauchbar.
Für akademische Zwecke ist es jedoch durchaus möglich, eine Funktion zu entwickeln, die die volle Funktionalität nutzen kann:
 func printShelf(with iterator: inout ShelfIterator) { var bookIndex = 0 while !iterator.isDone { bookIndex += 1 print("\(bookIndex). \(iterator.currentItem!.author) – \(iterator.currentItem!.title)") _ = iterator.next() } } var iterator = ShelfIterator(books: shelf.books) printShelf(with: &iterator)  
Der 
iterator Parameter wird als 
inout deklariert, weil Sein interner Zustand ändert sich während der Ausführung der Funktion. Und wenn die Funktion aufgerufen wird, wird die Iteratorinstanz nicht direkt durch ihren eigenen Wert übertragen, sondern als Referenz.
Das Ergebnis des Aufrufs der 
next() -Methode wird nicht verwendet, wodurch das Fehlen des Rückgabewerts einer Lehrbuchimplementierung simuliert wird.
Anstelle einer Schlussfolgerung
Dies scheint alles zu sein, was ich diesmal sagen wollte. Alles schöne Code und absichtliches Schreiben!
Meine anderen Artikel zu Designmustern: