2014-12-08 24 views
6

Kann ich eine Array-Erweiterung erstellen, die z. B. nur für Strings gilt?Ist es möglich, eine Array-Erweiterung in Swift zu machen, die auf eine Klasse beschränkt ist?

+1

Nicht sicher, was Sie meinen - können Sie mehr über Ihre Absicht sagen? Vielleicht ein Beispiel? – dpassage

+0

@ dpassage Sie können Dinge mit der Anforderung erweitern, dass die erweiterten Klassen ein Protokoll einhalten (https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Extensions.html), ich bin neugierig wenn Sie es weiter auf eine Klasse beschränken können, so dass meine Erweiterung in Swift nur für String-Arrays gilt. –

+0

Aber beschreiben Sie, was Sie eine solche Erweiterung zu _do_ wollen. Geht es nur um eine Funktion? Definieren Sie dann eine globale generische Funktion. Geben Sie einen tatsächlichen Anwendungsfall für das, was Sie erreichen möchten. – matt

Antwort

12

Ab Swift 2, das jetzt mit Protokollerweiterungen werden kann , erreicht, die zu konformen Typen Verfahren und Eigentum Implementierungen (gegebenenfalls mit zusätzlichen Einschränkungen eingeschränkt).

Ein einfaches Beispiel: Definieren ein Verfahren für alle Arten konformen zu SequenceType (wie Array), wo das Sequenzelement ist ein String:

extension SequenceType where Generator.Element == String { 
    func joined() -> String { 
     return "".join(self) 
    } 
} 

let a = ["foo", "bar"].joined() 
print(a) // foobar 

Die Erweiterungsmethode nicht direkt für struct Array definiert werden kann, aber nur, für alle Typen konform zu einigen Protokoll (mit optionalen Einschränkungen). So muss man ein Protokoll finden, dem Array entspricht und das alle notwendigen Methoden bereitstellt. Im obigen Beispiel ist das SequenceType.

Ein anderes Beispiel (eine Variation von How do I insert an element at the correct position into a sorted array in Swift?):

extension CollectionType where Generator.Element : Comparable, Index : RandomAccessIndexType { 
    typealias T = Generator.Element 
    func insertionIndexOf(elem: T) -> Index { 
     var lo = self.startIndex 
     var hi = self.endIndex 
     while lo != hi { 
      // mid = lo + (hi - 1 - lo)/2 
      let mid = lo.advancedBy(lo.distanceTo(hi.predecessor())/2) 
      if self[mid] < elem { 
       lo = mid + 1 
      } else if elem < self[mid] { 
       hi = mid 
      } else { 
       return mid // found at position `mid` 
      } 
     } 
     return lo // not found, would be inserted at position `lo` 
    } 
} 

let ar = [1, 3, 5, 7] 
let pos = ar.insertionIndexOf(6) 
print(pos) // 3 

Hier wird das Verfahren als eine Erweiterung CollectionType definiert ist, weil Subskript Zugriff auf die Elemente benötigt wird, und die Elemente werden Comparable sein erforderlich.

+0

Sie können 'Index == Int' zu' Index: RandomAccessIndexType' mit einigen kleineren Änderungen, z. 'startIndex' anstelle von' 0', 'endIndex.predecessor()', 'let mid = lo.advancedBy (lo.distanceTo (hi)/2)' plus einen vorzeitigen Bail-On 'isEmpty'. Würde bedeuten, dass es zum Beispiel mit 'String.UTF16View' arbeiten würde, das zufällig, aber nicht ganz indiziert ist. –

+0

@AirspeedVelocity: Fertig :) –

5

UPDATE: Bitte lesen Sie Martins Antwort unten für Swift 2.0 Updates. (Ich kann diese Antwort nicht löschen, da es akzeptiert wird, wenn Doug kann Martins Antwort akzeptieren, ich werde diese eine löschen zukünftige Verwirrung zu vermeiden.)


Dies hat mehrere Male in den Foren kommen, und Die Antwort ist nein, das kannst du heute nicht tun, aber sie verstehen, dass es ein Problem ist und sie hoffen, dass es in Zukunft verbessert wird. Es gibt Dinge, die sie zur stdlib hinzufügen möchten, die das auch brauchen. Deshalb gibt es so viele kostenlose Funktionen wie stdlib. Die meisten von ihnen sind Work-arounds für entweder dieses Problem oder das Problem "keine Standardimplementierung" (d. H. "Merkmale" oder "Mixins").

+2

Danke, es schien wie eine seltsame Unterlassung. Wird meine Daumen drücken. –

+0

Möchte diesen für 2.0 bearbeiten, insb. wie Dupes sich darauf beziehen. –

0

Sie haben trotz vieler Anfragen in Kommentaren immer noch keinen Anwendungsfall angegeben, daher ist es schwer zu wissen, wonach Sie suchen. Aber, wie ich bereits in einem Kommentar gesagt habe (und Rob hat in einer Antwort gesagt), werden Sie es nicht wörtlich bekommen; Erweiterungen funktionieren auf diese Weise nicht (im Moment).

Wie ich in einem Kommentar sagte, was ich tun würde, ist das Array in eine Struktur zu wickeln. Jetzt schützt und schützt die Struktur den Typ der Zeichenfolge und wir haben eine Kapselung. Hier ist ein Beispiel, obwohl man natürlich im Auge behalten muß, dass Sie keinen Hinweis auf die Art der Sache gegeben haben würden Sie wirklich zu tun, so dass dies möglicherweise nicht direkt befriedigend:

struct StringArrayWrapper : Printable { 
    private var arr : [String] 
    var description : String { return self.arr.description } 
    init(_ arr:[String]) { 
     self.arr = arr 
    } 
    mutating func upcase() { 
     self.arr = self.arr.map {$0.uppercaseString} 
    } 
} 

Und hier ist, wie zu nennen:

let pepboys = ["Manny", "Moe", "Jack"] 
    var saw = StringArrayWrapper(pepboys) 
    saw.upcase() 
    println(saw) 

So haben wir unser String-Array in eine Welt effektiv isoliert, wo wir es mit Funktionen bewaffnen können, die nur für String-Arrays anwenden. Wenn pepboys kein Zeichenfolgenarray wäre, hätten wir es nicht zuerst in einen StringArrayWrapper einschließen können.

+0

Wenn das Array wahrscheinlich groß ist, ist eine Klasse möglicherweise besser als eine Struktur, so dass Ihnen Pass-by-Reference garantiert ist. Aus diesem Grund hänge ich oft große Arrays in Klassen, so dass man sie nicht kopieren muss. – matt

+0

Swift verwendet Copy-on-Write für Pass-by-Value, daher sollten in den meisten Fällen keine großen Kosten bei der Übergabe großer Arrays entstehen. Wenn Sie große Kosten finden, sollten Sie ein Radar öffnen. Apple hat ausdrücklich darauf hingewiesen, dass Sie aus Leistungsgründen nicht versuchen sollten, dem Kopieren auszuweichen. Siehe den Hinweis am Ende von https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html –

+0

@RobNapier Die Eigenschaft hier ist eine 'var' und das Beispiel geht davon aus, dass dies der Fall ist geschrieben werden. – matt

1

Dies wurde bereits von den drei Weisen oben beantwortet ;-), aber ich biete demütig eine Verallgemeinerung von @ Martins Antwort an. Wir können eine beliebige Klasse tarnen, indem wir das Protokoll "marker" verwenden, das nur für die Klasse implementiert ist, auf die wir zielen wollen. Ie. man muss nicht finden ein Protokoll per se, aber kann ein triviales für die Verwendung in der Ausrichtung der gewünschten Klasse erstellen.

protocol TargetType {} 
extension Array:TargetType {} 

struct Foo { 
    var name:String 
} 

extension CollectionType where Self:TargetType, Generator.Element == Foo { 
    func byName() -> [Foo] { return sort { l, r in l.name < r.name } } 
} 

let foos:[Foo] = ["c", "b", "a"].map { s in Foo(name: s) } 
print(foos.byName()) 
Verwandte Themen