2014-08-31 6 views
15
protocol P : class { 
    var value:Int {get} 
} 

class X : P { 
    var value = 0 

    init(_ value:Int) { 
     self.value = value 
    } 
} 

var ps:[P] = [X(1), X(2)] 
for p in ps { 
    if let x = p as? X { // works for a single variable 
     ... 
    } 
} 

if let xs = ps as? [X] { // doesn't work for an array (EXC_BAD_ACCESS) 
    ... 
} 

Jeśli P jest klasą zamiast protokołu, to kod działa poprawnie. Jaka jest różnica między klasą a protokołem? Oba są zaimplementowane jako wskaźniki w stercie, prawda? Powyższy kod można skompilować pomyślnie, ale awarię w czasie wykonywania. Co oznacza ten błąd EXC_BAD_ACCESS?Tablica z wpisaną tablicą nie może być w dół do konkretnej tablicy typów


Dzięki @Antonio, ale nadal nie rozumiem, jak działa ten przykładowy kod.

let someObjects: [AnyObject] = [ 
    Movie(name: "2001: A Space Odyssey", director: "Stanley Kubrick"), 
    Movie(name: "Moon", director: "Duncan Jones"), 
    Movie(name: "Alien", director: "Ridley Scott") 
] 
for movie in someObjects as [Movie] { 
    println("Movie: '\(movie.name)', dir. \(movie.director)") 
} 

Czy AnyObject to specjalny przypadek?

referencyjny: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html#//apple_ref/doc/uid/TP40014097-CH22-XID_498


protocol P { 

} 

@objc class X : P { 

} 

@objc class Y : X { 

} 

var xs:[X] = [Y(), Y()] 
var ps:[P] = [Y(), Y()] 


xs as? [Y] // works 
ps as? [Y] // EXC_BAD_ACCESS 

Próbowałem ten kod w plac zabaw. Ponieważ jest to czysty, szybki kod, myślę, że nie ma to nic wspólnego z @objc.

+0

Zaktualizowałem moją odpowiedź - poprzednia była wyraźnie błędna. – Antonio

+0

Nadal nie rozumiem, dlaczego operator 'jest' i' jak? 'Działa dla pojedynczej zmiennej, ale nie dla tablicy. Ale można to teraz rozwiązać za pomocą 'flagMap'. –

Odpowiedz

16

Ignorując opcjonalny wiążące na chwilę i za pomocą bezpośredniego przypisania:

let x = ps as [X] 

następujący błąd Runtime jest donosi:

fatal error: array element cannot be bridged to Objective-C 

Oznacza to, że przygnębiony z gamy protokołów do tablicy adopcyjnych wymaga powiązania obj-c. Można to łatwo rozwiązać poprzez uznanie protokołu jako objc kompatybilne:

@objc protocol P : class { 
    var value:Int {get} 
} 

Z tej prostej zmiany, kod działa teraz, a nie wyjątek czas prowadzony jest podniesiony.

Teraz , jak jest rozwiązany, ale pozostawiając , dlaczego otwarty problem. Nie mam jeszcze odpowiedzi, ale spróbuję głębiej to zrozumieć.

Uzupełnienie: dowiedzieć się „dlaczego”

spędziłem jakiś czas śledztwo w tej sprawie, i po co ja przyszedłem z.

Mamy protokół i klasa przyjmując go:

protocol P {} 
class X : P {} 

tworzymy tablicę t:

var array = [P]() 

Konwersja pustą tablicę do [X] działa:

array as [X] // 0 elements 

Jeśli dodamy element do tablicy, wystąpi błąd środowiska wykonawczego:

array.append(X()) 
array as [X] // Execution was interrupted, reason: ... 

Wyjście konsola mówi, że:

fatal error: array element cannot be bridged to Objective-C 

Więc odlewania tablicę obiektów protokół do tablicy jej przysposabiającego wymaga pomostowego. To usprawiedliwia, dlaczego @objc rozwiązuje problem:

@objc protocol P {} 
class X : P {} 

var array = [P]() 
array.append(X()) 
array as [X] // [X] 

Przesiewając dokumentację, dowiedziałam się, dlaczego tak się stało.

Aby wykonać rzutowanie, środowisko wykonawcze musi sprawdzić, czy X jest zgodne z protokołem . documentation wyraźnie stwierdza, że:

Można sprawdzić protokół zgodności tylko wtedy, gdy protokół jest oznaczony @objc atrybutu

Aby zweryfikować (nie, że nie mam zaufania do dokumentacji), Użyłem tego kodu na placu zabaw:

protocol P {} 
class X : P {} 

let x = X() 
let y = x is P 

ale pojawia się inny błąd, stwierdzając, że:

Playground execution failed: <EXPR>:18:11: error: 'is' test is always true 
let y = x is P 

piśmie, że w „zwykłej” projektu zamiast mamy co oczekiwać:

protocol P {} 
class X {} 

func test() { 
    let x = X() 
    let y = x is P 
} 

Cannot downcast from 'X' to [email protected] protocol type 'P' 

Wniosek: zamówienie na protokole wpisany tablicy być przybity do konkretnego typu tablicy, protokół musi być oznaczone @objc atrybut. Powodem jest to, że środowisko wykonawcze używa operatora is w celu sprawdzenia zgodności protokołu, który zgodnie z dokumentacją jest dostępny tylko dla protokołów mostkowych.

+0

(W odniesieniu do twoich komentarzy do teraz skasowanego pytania http://stackoverflow.com/questions/25589605/swift-forced-upwrapping-of-array-of-optionals: Jest całkowicie OK, aby odpowiedzieć na twoje własne pytanie i ludzi Zachęcamy do dzielenia się swoją wiedzą. Zobacz: http://stackoverflow.com/help/self-answer, aby uzyskać więcej informacji.) –

+0

Dzięki @MartinR - zawsze widziałem ludzi publikujących komentarze takie jak moje, więc pomyślałem, że to nie dozwolony. Przeprowadził (prawdopodobnie zbyt szybkie) wyszukiwanie i nie znalazł niczego jawnie mówiącego, że jest dozwolone lub zniechęcone. Spróbuję do niego dotrzeć dzięki moim apoligiom ... – Antonio

+0

To jest złe. Jeśli 'A' jest podklasą' B', '[A]' jest niejawnie wymienialne na '[B]', a '[B]' może być jawnie rzutowane na '[A]'.Ani 'Int', ani' UInt' nie są podklasami siebie nawzajem (w rzeczywistości żadna z nich nie jest klasą, nie mówiąc już o podklasach). – newacct