Ответ 1
Игнорирование необязательной привязки на мгновение и использование прямого назначения:
let x = ps as [X]
сообщается о следующей ошибке выполнения:
fatal error: array element cannot be bridged to Objective-C
Это означает, что downcast из массива протоколов в массив усыновителей требует привязки 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
для проверки соответствия протокола, который в соответствии с документацией доступен только для мостовых протоколов.