2015-07-29 5 views
5

[Jede] als Parameter nimmt Ich habe ein Array des Typs [String]keinen Array-Typen zu einer Funktion passieren kann, die

let names = ["Joffrey", "Cersei", "Mountain", "Hound"] 

I habe eine Funktion, die eine Reihe von [Any] Typ erfolgt.

func printItems(items: [Any]){ 
    for item in items { 
     print(item) 
    } 
} 

Nun, wenn ich rufe die Funktion mit names als Parameter,

printItems(names) 

ich einen Fehler Kann 'printItems' mit einem Argument Liste vom Typ '([String])' aufzurufen.

Any ist nur ein typealias für ein Protokoll, das alle Typen implizit entsprechen.

Gedanken?

+0

Daumen nach oben für Namen im Array :) –

+0

Kann nicht für eine schnelle sprechen, aber in den meisten Sprachen könnte dies ein Problem sein, weil 'printItems' das Array von' Any' nehmen könnte und ändern Sie eines der Elemente zu - nun - alles, was es wollte (etwas anderes als eine Zeichenfolge). Aber der Aufrufer denkt, dass sie ein Array von "Strings" haben, so dass Probleme auftreten können, wenn "printItems" beendet ist. Gilt das für schnelle? –

+0

Was passiert, wenn Sie den Typ Ihres Namensarrays in '[Beliebige]' ändern –

Antwort

2

Dies ist eine überraschende Einschränkung von Swift. Sie können ein Array nicht in den Typ [Any] eingeben, sodass Sie es nicht an eine Funktion übergeben können, die den Typ [Any] übernimmt. Sie können map verwenden, um jedes Element des Arrays zu werfen:

printItems(names.map {$0 as Any}) 

Aber der richtige Weg, dies in Swift zu tun ist, verwenden Generics:

func printItems<T>(items: [T]) { 
    for item in items { 
     print(item) 
    } 
} 

let names = ["Joffrey", "Cersei", "Mountain", "Hound"] 
let numbers = [3.1416, 2.71818, 1.4142, 1.618034] 

printItems(names) // This now works 
printItems(numbers) // This works too 
+1

für die generische Empfehlung –

+0

Danke für die generische Antwort. Aber ich nehme eine Session über Generika. Ich wollte wissen, warum das scheitert. Ich sollte mit einer Antwort darauf vorbereitet sein. –

+0

@ AirspeedVelocity Antwort sagt Ihnen, warum dies fehlschlägt. Sie sollten abstimmen und seine Antwort akzeptieren. – vacawama

0

Sie müssen explizit in Ihrer let Erklärung zu setzen dass Sie ein Beliebiges Array deklarieren und kein bestimmtes Array.

Mit Ihrer aktuellen Deklaration wird Swift das Array [String] auswerten. und nicht als [Any] -Array.

zu beheben Ihr Problem nur Folgendes tun:

let names : [Any] = ["Joffrey", "Cersei", "Mountain", "Hound"] 

Wenn Sie Ihre let Erklärung beibehalten möchten, verwenden ANYOBJECT in Ihrer printItems Funktion und es wird von Swift akzeptiert.

func printItems(items: [AnyObject]){ 
    for item in items { 
     print(item) 
    } 
} 
+0

"Wenn Sie Ihre Let-Deklaration beibehalten möchten, verwenden Sie AnyObject in Ihrer printItems-Funktion und es wird von Swift akzeptiert." Dies ist ein wenig irreführend. Was hier passiert, ist, dass Swift's automatische Überbrückung einsetzt und das gesamte Array konvertiert zu 'NSString'-Objekten.Dies funktioniert nur mit Strings, nicht beliebigen Typen, und stellt keine allgemeine Lösung dar. Beachten Sie auch, dass dies nur funktioniert, wenn Foundation importiert wurde .. –

1

Während jede Art Any entspricht, dann ist dies nicht das gleiche wie es eine universelle implizite geordnete Klasse zu sein, dass alle Arten von erben.

Wenn Sie einen Typ in ein Protokoll umwandeln, erstellen Sie einen neuen Wert mit einer anderen Struktur.Also für eine Reihe von Typ sein Any, muss er physisch von der String Darstellung umgewandelt werden:

sizeof(String) // 24 bytes (on 64-bit, anyway) 

zur Any Darstellung:

sizeof(Any) // 32 bytes, includes some meta data 
      // about what the type really is 

Da Werttypen direkt im Array gehalten werden, das Array würde eine ganz andere Form haben, so dass der Compiler unter der Haube das Äquivalent dazu hätte:

Swift könnte diesen Prozess möglicherweise für Sie automatisieren (wenn Sie eine einzelne Variable an eine Funktion übergeben, die Any dauert). Aber persönlich bin ich froh, dass es nicht ist, ich würde eher das expliziter sein - nehmen Sie an, dass Ihr Array riesig war, würde dies eine Menge von Verarbeitung implizit unter der Haube passieren.

Dies unterscheidet sich von, wenn Sie eine Reihe von Referenztypen haben, von denen alle Zeiger auf die aktuellen Daten sind und so alle die gleiche Größe und die brauchen keine Transformation, wenn Upcasting:

class C { } 
class D: C { } 

let d = D() 
let c: C = d 
unsafeBitCast(d, UnsafePointer<Void>.self) // these value will 
unsafeBitCast(c, UnsafePointer<Void>.self) // be the same 

So sagen „diese Reihe von [D] ist wirklich eine Reihe von [C]“ nur eine Frage des Compilers ist die Art Vereinbarung ersetzt werden kann, braucht keine Datentransformation zu:

// so this works fine, 
// no runtime transformation needed: 
func f(cs: [C]) { } 
let ds = [D(),D()] 
f(ds) 

Aber Protokolle sind noch anders Super Referenzen, wenn sie mit Klassen verwendet:

protocol P { } 
extension C: P { } 

sizeofValue(C())  // 8 bytes (just a pointer) 
sizeofValue(C() as P) // 40 bytes 

func g(ps: [P]) { } 
g(ds) // won’t compile, needs transformation 
+0

für die Erläuterung der inneren Funktionsweise von Arrays – vacawama

+0

Der zweite Absatz ist irreführend Jeder Referenztyp, der vom Typ 'Any' sein soll, muss ebenfalls physikalisch transformiert werden, weißt du? – rintaro

+2

@rintaro guter Punkt, wird reword - mein Punkt war mehr, dass Swift anders arbeitet als zB Java, wo' Object' steht Universelle Unterklasse –

Verwandte Themen