Почему API Objective-C возвращают неявно развернутые опции?

Я довольно озадачен этим. Если мы возьмем метод cellForRowAtIndexPath: в UITableView, например, его подпись метода:

func cellForRowAtIndexPath(_ indexPath: NSIndexPath!) -> UITableViewCell!

И его возвращаемое значение:

Объект, представляющий ячейку таблицы или nil, если ячейка не видна, или indexPath вне диапазона.

Это звучит как идеальная причина использовать стандартный вариант. Фактически, поскольку все типы, основанные на указателях в Objective-C, могут быть nil... кажется, имеет смысл, что все типы указателей Objective-C должны быть импортированы в качестве стандартных опций.

Я знаю из разговоров WWDC, что они говорят, что для неявно развернутых опций:

  • Может быть проверено явно для nil
  • Может напрямую обращаться к свойствам/методам базового значения
  • Может быть неявно преобразовано в его базовое значение

И от Apple С помощью Swift с Cocoa и Objective-C:

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

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

Вместо того, чтобы давать ошибку времени компиляции или предупреждение, они решили, что ошибка во время выполнения была лучше? Это очень запутанно.

Учитывая, что ничто не отвечает на этот вопрос, который я видел... Я думаю, что это что-то очевидное для всех, что я просто не вижу, но... Почему так?

Действительно ли это просто для того, чтобы люди не могли использовать if let или необязательную цепочку при использовании API Objective-C в Swift или что-то еще?

Ответ 1

Когда вы делаете неявно разворачиваемую опцию в Swift, это не значит, что она всегда будет не нулевой: все это означает, что вы сообщаете компилятору, что при доступе к их свойствам вы ожидаете, что объект будет не- nil. Объект, на который вы ссылаетесь, может быть явно проверен на nil; установка его на nil также не приведет к исключению, если вы не попытаетесь получить доступ к каким-либо его свойствам после этого.

Когда Apple использовала неявно развернутые опции для параметров

func tableView(_ tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!

они позволяют сэкономить несколько дополнительных if - let. В этом случае они знают, что они никогда не передают вам nil; в других случаях они этого не знают, и они ожидают, что вы nil - проверите объект.

Они позволяют вам возвращать nil. Это зависит от них, чтобы проверить результаты для nil, если, конечно, вы не решите называть эту функцию самостоятельно. Хотя я не могу придумать правильную причину для вызова cellForRowAtIndexPath из вашего собственного кода, если вы сделаете звонок, вы должны будете проверить возвращаемое значение для nil.

Если вы рассмотрите альтернативу создания параметров UITableView? и NSIndexPath?, все реализации должны будут либо использовать восклицательный знак после tableView и indexPath, либо использовать if - let идиома. По сравнению с этим выбором, неявно развернутые типы выглядят как лучший выбор.

Ответ 2

Ниже было ответ Грега Паркера в swift-users на lists.swift.org:

Импорт как неявно-развернутый необязательный - это удобство использования компромисс. Большинство указателей Objective-C никогда не равны нулю. Если указатель равен нулю, и автор не проверял, то процесс намеренно останавливается. Это не хуже, чем поведение, которое вы получаете, когда запись Objective-C код.     Импорт IUO предназначен для остановки. я n в долгосрочной перспективе каждый интерфейс Objective-C должен быть явно аннотирован, чтобы Swift могут импортировать их более точно. В вашем собственном коде вы можете использовать NS_ASSUME_NONNULL_BEGIN/END в ваших файлах заголовков. Каждый не аннотированный указатель объекта внутри этих маркеров не равен.