2016-06-25 2 views
6

Von Apple's MutableCollection API reference:MutableCollection vs RangeReplaceableCollection

Das MutableCollection Protokoll erlaubt die Werte ein Element der Sammlung zu ändern, aber nicht die Länge der Sammlung selbst. Bei Operationen, die Elemente erfordern das Hinzufügen oder Entfernen finden Sie in das RangeReplaceableCollection Protokoll statt.

erfordert jedoch MutableCollection den folgenden Index:

subscript(bounds: Range<Self.Index>) -> Self.SubSequence { get set } 

Enthält diese die Länge der Sammlung nicht ändern lassen? Zum Beispiel könnten wir diesen Index des Setter mit einem leeren Bereich nicht nennen und nicht leerer Teilfolge?

Antwort

5

Kurze Antwort:

Wenn Sie eine Variable des MutableCollection Art haben, dann müssen Sie den Index Setter nennen nur mit einer Reihe und eine neue Scheibe mit der gleichen Länge. Einige Typen MutableCollection (wie Array) erlauben eine unterschiedliche Länge Ersatzelemente einfügen oder löschen konforme, aber im allgemeinen eine veränderbare Sammlung braucht das nicht zulassen.

Insbesondere die Standardimplementierung des MutableCollection Index Setter bricht mit einer Laufzeitausnahme, wenn der Bereich und die neue Scheibe nicht die gleiche Länge haben.

Lange Antwort:

Zunächst ist zu beachten, dass Sie zu

public subscript(bounds: Range<Index>) -> MutableSlice<Self> 

in Ihrer eigenen Sammlung nicht zu implementieren, da es eine Standardimplementierung in eine Protokollerweiterung hat. Wie man in der source code dieses Verfahrens sehen kann, ruft das Subskript setter eine

internal func _writeBackMutableSlice() 

Funktion, die here implementiert ist. , dass die gemeinsame Anzahl von Elementen aus der Scheibe zum Zielbereich ersten Kopien funktionieren, überprüft und dann, dass der Index-Bereich und die neue Scheibe die gleiche Länge haben:

_precondition(
    selfElementIndex == selfElementsEndIndex, 
    "Cannot replace a slice of a MutableCollection with a slice of a smaller size") 
_precondition(
    newElementIndex == newElementsEndIndex, 
    "Cannot replace a slice of a MutableCollection with a slice of a larger size") 

So können Sie nicht die Länge einer Änderung MutableCollection über den (Standard) Index Setter, und zu versuchen, so zu tun, wird das Programm abgebrochen.

Als Beispiel nehmen wir Gebrauch MutableCollection eine „minimal“ Typ entspricht, definieren:

struct MyCollection : MutableCollection, CustomStringConvertible { 

    var storage: [Int] = [] 

    init(_ elements: [Int]) { 
     self.storage = elements 
    } 

    var description: String { 
     return storage.description 
    } 

    var startIndex : Int { return 0 } 
    var endIndex : Int { return storage.count } 

    func index(after i: Int) -> Int { return i + 1 } 

    subscript(position : Int) -> Int { 
     get { 
      return storage[position] 
     } 
     set(newElement) { 
      storage[position] = newElement 
     } 
    } 
} 

Dann Ersetzen eines Teils der Sammlung mit einer Scheibe von der gleichen Länge Werke:

var mc = MyCollection([0, 1, 2, 3, 4, 5]) 
mc[1 ... 2] = mc[3 ... 4] 
print(mc) // [0, 3, 4, 3, 4, 5] 

Aber für verschiedene Längen bricht es mit einer Laufzeitausnahme ab:

mc[1 ... 2] = mc[3 ... 3] 
// fatal error: Cannot replace a slice of a MutableCollection with a slice of a smaller size 
Hinweis

, dass konkrete Typen MutableCollectionkann konformen ermöglicht unterschiedliche Längen Ersatz in ihrem Index Setter, wie zum Beispiel Array tut.

+0

Also, wenn ich 'tiefgestellt (Bounds:)' selbst implementieren soll (zum Beispiel, wenn ich etwas anderes als 'MutableSlice ' will), sollte ich nur 'assert' verwenden, um sicherzustellen, dass die Anzahl der Elemente übereinstimmt? –

+0

@TimVermeulen: Ich bin mir nicht 100% sicher, aber ich denke du darfst eine andere Anzahl von Elementen akzeptieren. Zum Beispiel entspricht "Array" auch "MutableCollection", hat jedoch einen eigenen Setter, der das Einfügen oder Löschen von Elementen ermöglicht. - Jede Funktion, die einen Parameter 'MutableCollection' annimmt, muss davon ausgehen, dass nur Ersetzungen gleicher Länge möglich sind. –

+0

Interessant. Ich denke, es ist nur verwirrend, weil Swifts Typsystem nicht erzwingen kann, dass die gegebene Teilsequenz die richtige Anzahl von Elementen haben muss (und weil die Dokumentation es auch nicht sehr gut erklärt). Danke vielmals! –