2014-08-31 2 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) 
    ... 
} 

Если P является классом вместо протокола, то код работает правильно. В чем разница между классом и протоколом? Они оба реализованы как указатели в куче, не так ли? Вышеупомянутый код может быть скомпилирован успешно, но сбой во время выполнения. Что означает эта ошибка EXC_BAD_ACCESS?протокол типизированного массива не может быть преобразован в массив конкретного типа

Благодаря @Antonio, но я до сих пор не понимаю, как работает этот образец кода.

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)") 
} 

Is AnyObject специальный чехол?

ссылка: 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 

Я попробовал этот код в игровую площадку. Поскольку это чистый быстрый код, я думаю, что он не имеет ничего общего с @objc.

+0

Я обновил свой ответ - предыдущий явно был неправ. – Antonio

+0

Я до сих пор не понимаю, почему оператор 'is' и' as? 'Работает для одной переменной, но не для массива. Но это можно решить с помощью 'flagMap'. –

ответ

16

Игнорирование дополнительного связывания на минуту и ​​с помощью прямого назначения: сообщается

let x = ps as [X] 

следующее сообщение об ошибке времени выполнения:

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

Это означает, что опущенными из массива протоколов к массиву усыновителей требует связывания obj-c. Это может быть легко решена, объявив протокол, совместимый ObjC:

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

С этим простым изменением, код теперь работает и нет времени запуска исключение не возбуждается.

Теперь как решается, но оставляя , почему открытая проблема. У меня пока нет ответа, но я попытаюсь углубиться в это.

Добавление: фигура из «почему»

Я провел некоторое время расследование по этому вопросу, и следующее, что я пришел с.

У нас есть протокол и класс его принятия:

protocol P {} 
class X : P {} 

Мы создаем массив P:

var array = [P]() 

Преобразование пустой массив [X] работ:

array as [X] // 0 elements 

Если мы добавим элемент в массив, произойдет ошибка времени выполнения:

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

Выход консоли говорит, что:

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

Так отливку массив объектов протокола к массиву его усыновителем требует преодоления. Это оправдывает почему @objc исправляет проблему:

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

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

просеивания документацию, я узнал причину, чтобы это произошло.

Для выполнения литья время выполнения должно проверить, соответствует ли X протоколу P. documentation четко говорится, что:

Вы можете проверить для протокола соответствия, только если протокол помечен атрибутом @objc

Чтобы проверить, что (не то, что я не доверяю документации), Я использовал этот код на детской площадке:

protocol P {} 
class X : P {} 

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

, но я получаю другую ошибку, заявив, что:

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

Дать, что в «обычном» проекта вместо этого мы получаем то, что ожидалось:

protocol P {} 
class X {} 

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

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

Вывод: для того, для массива протокольного напечатал быть опущенными в массив конкретного типа, протокол должен быть маркированы @objc атрибут. Причина в том, что среда выполнения использует оператор is для проверки соответствия протокола, который, согласно документации, доступен только для мостовых протоколов.

+0

(Что касается ваших комментариев к удаленному вопросу http://stackoverflow.com/questions/25589605/swift-forced-upwrapping-of-array-of-optionals: вполне нормально отвечать на ваш собственный вопрос, а люди вам предлагается поделиться своими знаниями, сделав это. См. http://stackoverflow.com/help/self-answer для получения дополнительной информации.) –

+0

Спасибо @MartinR - Я всегда видел, как люди отправляли комментарии, подобные моим, поэтому я подумал, что это не допускается. Был (возможно, слишком быстрый) поиск и не мог найти ничего явно, говоря, что это разрешено или обескуражено. Я попытаюсь связаться с ним с моими аполигами ... – Antonio

+0

Это неправильно. Если 'A' является подклассом' B', '[A]' неявно конвертируется в '[B]', а '[B]' может быть явно передан в '[A]'.Ни 'Int', ни' UInt' не являются подклассами друг друга (фактически, ни один из них не является классами, не говоря уже о подклассах). – newacct