«Фатальная ошибка: нельзя соединить массив с Objective-C» - зачем вы вообще пытаетесь, Swift?

92

Я объявил протокол Swift:

protocol Option {
    var name: String { get }
}

Я объявляю несколько реализаций этого протокола - некоторые классы, некоторые перечисления.

У меня есть контроллер представления со свойством, объявленным так:

var options: [Option] = []

Когда я пытаюсь установить это свойство для массива объектов, реализующих Optionпротокол в другом VC prepareForSegue, я получаю ошибку времени выполнения:

fatal error: array cannot be bridged from Objective-C

Почему это не работает? У компилятора есть вся необходимая информация, и я вообще не понимаю, какое отношение к нему имеет Objective-C - мой проект содержит только файлы Swift, и эти массивы не входят или выходят из каких-либо методов фреймворка, которые могли бы заставляют их соединяться NSArray.

Роберт Аткинс
источник
6
Вы пытались подготовить @objcсвой протокол? stackoverflow.com/a/28029568/377369
Фабио Полони
1
Это не работает, если какая-либо из реализаций протокола является перечислением: «Неклассовый тип 'Foo' не может соответствовать протоколу класса 'Option'»
Роберт Аткинс
Но почему это должен быть протокол класса? Я не передаю его в инфраструктуру Obj-C или что-либо еще, для чего требуется, чтобы Swift Array был подключен к NSArray.
Роберт Аткинс,
То, как Swift и Objective-C работают вместе, до сих пор для меня секрет. Мне просто нужно «принять» многие вещи, которые просто «работают» или «не работают».
Фабио Полони
9
Почему у этого так много голосов против? Для меня это честный и ясный вопрос.
Guven

Ответы:

83

Я нашел решение. Это довольно ... неудовлетворительно , но работает. Где я устанавливаю массив на контроллере представления назначения, я делаю:

destinationViewController.options = options.map({$0 as Option})
Роберт Аткинс
источник
вы не можете использовать весь массив? options as [Option]
Константин Коваль
Неа. Пробовал (Xcode 6.3.1 (6D1002)), не работает. Мне не нужно приводить его в любом случае, компилятор знает, что я передаю массив вещей, реализующих Option.
Роберт Аткинс,
2
«Массив вещей, реализующих Option» Ах, но это не то же самое, что массив Option, который вам нужен. Смотрите мой ответ.
Мэтт
1
Это работает, и да, это очень неудовлетворительно ... в этом не должно быть необходимости. Swift должен уметь обрабатывать htis.
Оскар Гомес,
Я согласен ... это работает так, но это очень неудовлетворительный фрагмент кода
Майкл
22

компилятор знает, что я передаю массив вещей, реализующих Option

Вы произнесли очень показательное замечание, указывающее на источник проблемы. «Массив вещей, реализующих Option» не является массивом Option.

Проблема заключается в том, какой тип optionsспинки находится в том месте, где вы его создаете (в prepareForSegue). Вы не показываете этот код, но я держу пари, что вы не сможете его ввести / ввести в этот момент. Вот почему задание не выполняется. optionsможет быть множество вещей, которые действительно могут принять Option, но этого недостаточно; он должен быть набран как массив Option.

Итак, вернемся к следующей prepareForSegueформе options:

let options : [Option] = // ... whatever ...

Теперь вы можете назначить его напрямую destinationViewController.options.

Вот небольшой тестовый пример (на детской площадке; я ненавижу детские площадки, но они могут найти свое применение):

protocol Option {
    var name : String {get}
}

class ViewController : UIViewController {
    var options : [Option] = []
}

enum Thing : Option {
    var name : String {
        get {
            return "hi"
        }
    }
    case Thing
}

let vc = ViewController()
let options : [Option] = [Thing.Thing]
vc.options = options // no problem

(Я также тестировал это в реальном приложении с реальным prepareForSegue, и он отлично работает.)

матовый
источник
1
Я думаю, что это в высшей степени нарушено, потому что компилятор действительно знает во время выполнения, что Thing является опцией. И в любом случае, как указано в комментарии к моему собственному ответу ниже, ни casting ( viewController.options = things as [Option]), ни создание временной переменной, явно типизированной, [Option]как вы предлагаете здесь, на самом деле не работают. В обоих случаях я получаю ошибку времени выполнения.
Роберт Аткинс,
Затем вы должны объяснить, почему это работает для меня. Происходит что-то еще, о чем вы не заявили. Если вы не раскрываете больше кода, я просто должен подозревать, что вы скрываете что-то важное.
Мэтт
Может быть. Но я до сих пор не понимаю, какое отношение это имеет к Objective-C в первую очередь (по сравнению с исходной ошибкой времени выполнения). Я не делаю ничего (что я вижу), что должно заставить переходное приведение к NSArray.
Роберт Аткинс
2
Рассмотрим этот вариант. Я показал вам код, который работает. Вы не показали мне код, который не работает - я не могу воспроизвести вашу проблему из предоставленных данных. Помогите мне воспроизвести это.
Мэтт
1
@ CristiBăluță Это то, что вам нужно выяснить, прежде чем заявлять, что «эта проблема все еще не решена»
Мэтт
16

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

@objc protocol Option {
    var name: String { get }
}

Получил решение из этого ответа

Хуан
источник
1
Как и в комментариях к исходному вопросу, это не работает, если какой-либо из разработчиков протокола является Swift Enums. Что в моем случае они есть.
Роберт Аткинс,
опечатка obcj должна быть objc
Алан Скарпа
1

Этот тоже отлично работает

destinationViewController.options = options.map{$0}
Николай Денисюк
источник