2015-07-29 13 views
5

mam tablicę typu [String]nie mogą przenosić dowolny rodzaj tablicy do funkcji, która trwa [Dowolny] jako parametr

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

że posiada funkcję, która pobiera tablicę [Any] typu.

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

Teraz kiedy wywołać funkcję z names jako parametry,

printItems(names) 

pojawia się błąd nie może powoływać 'printItems' z listy argumentów typu '([String])'.

Any to tylko typealias dla protokołu, który wszystkie typy niejawnie spełniają.

Myśli?

+0

kciuki za nazwisk w tablicy :) –

+0

nie mogę mówić za szybki, ale w większości języków to może być problem, ponieważ 'printItems' może wziąć tę tablicę' Any' i zmień jeden z elementów na - cóż - wszystko, czego chciał (coś innego niż ciąg znaków). Ale wywołujący myśli, że ma tablicę 'Strings', więc może mieć problemy, gdy skończy się' printItems'. Czy to dotyczy szybkiego? –

+0

Co się stanie, jeśli zmienisz typ tablicy nazw na "[Any]" –

Odpowiedz

2

To zaskakujące ograniczenie Swift. Nie możesz rzucić tablicy, aby wpisać [Any], więc nie możesz przekazać jej do funkcji przyjmującej typ [Any]. Można użyć map rzucić każdy element tablicy:

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

Ale sposób prawo to zrobić w Swift jest użycie 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

dla ogólnego zalecenia –

+0

Dzięki za odpowiedź Generics. Ale biorę sesję na temat Generics. Chciałem wiedzieć, dlaczego to się nie udaje. Powinienem być przygotowany na odpowiedź. –

+0

@ Odpowiedź AirspeedVelocity informuje, dlaczego to się nie udaje. Powinieneś głosować i zaakceptować jego odpowiedź. – vacawama

0

Trzeba wyraźnie umieścić w deklaracji let że ty deklarujesz Dowolną tablicę, a nie konkretną tablicę typów.

Za pomocą bieżącej deklaracji Swift oceni ją na [Ciąg]. , a nie jako [Any] array.

Aby rozwiązać problemu po prostu wykonaj następujące czynności:

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

Jeśli chcesz zachować swoją deklarację let, użyj AnyObject w funkcji printItems i zostanie ona zaakceptowana przez Swift.

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

"Jeśli chcesz zachować deklarację let, użyj AnyObject w funkcji printItems, która zostanie zaakceptowana przez Swift." To jest trochę mylące.To się dzieje, że automatyczne mostkowanie Swifta rozpoczyna się i konwertuje całą tablicę do obiektów 'NSString' Działa to tylko z łańcuchami, a nie dowolnymi typami, i nie jest rozwiązaniem ogólnym.Należy również zauważyć, że działa to tylko jeśli podstawa została zaimportowana . –

1

Chociaż każdy typ jest zgodny Any, to nie jest takie samo jak bycie uniwersalnym niejawna nadklasą że wszystkie typy dziedziczą.

Podczas przesyłania typu do protokołu tworzysz nową wartość o innej strukturze.Tak na ciąg być typu Any, musi być fizycznie przekształcona z reprezentacją String:

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

do reprezentacji Any:

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

Ponieważ typy wartości odbywa się bezpośrednio w tablicy, tablica miałaby zupełnie inny kształt, więc pod maską kompilator musiałby wykonać równoważnik:

names.map { $0 as Any } // create a new array, with the Any versions 

Swift może prawdopodobnie zautomatyzować ten proces dla ciebie (ma to miejsce, jeśli przekazujesz pojedynczą zmienną do funkcji, która zajmuje Any). Ale osobiście cieszę się, że tak nie jest, wolałbym, żeby było to bardziej wyraźne - przypuśćmy, że twoja tablica była ogromna, to byłoby dużo przetwarzania dzieje się domyślnie pod maską.

Różni się to od kiedy masz tablicę typów referencyjnych, z których wszystkie są wskaźnikami do rzeczywistych danych i tak wszystkie tej samej wielkości, a które nie wymagają transformacji, gdy 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 

sposób mówić „ta tablica [D] jest naprawdę tablicą [C]” to tylko kwestia kompilatora akceptującej typy mogą być podstawione, transformacja danych nie musi odbywać się:

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

Ale protokoły nadal różnią się od nadklasy odniesienia w przypadku korzystania z zajęć:

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

dla wyjaśnienia wewnętrznego działania tablic – vacawama

+0

Drugi akapit Dowolny typ referencji typu "Any" również potrzebuje fizycznej transformacji, wiesz? – rintaro

+2

@rintaro dobry punkt, będzie reword - moim punktem było więcej, że Swift działa inaczej, na przykład, Java, gdzie 'Object' jest uniwersalna podklasa: –

Powiązane problemy