Я больше не вижу, как Xcode жалуется на то, что некоторые вещи нуждаются в дополнительных параметрах («?»). Теперь он всегда принудительно разворачивается (бах "!"). Есть ли какая-то причина использовать дополнительные параметры, когда мы принудительно развернули?
Почему Swift 2 предпочитает принудительную развертку дополнительным параметрам?
Ответы (2)
Я действительно не знаю, что вы имеете в виду, когда пишете, что больше не видите, как Xcode жалуется на то, что "некоторые вещи нуждаются в дополнительных параметрах. Теперь он всегда принудительно разворачивается". Эти два предложения противоречат друг другу:
- У вас может быть столько необязательных переменных, сколько вы пожелаете, но необязательные переменные могут быть очень полезными, когда вы узнаете все преимущества их использования.
- Если у вас есть свойство, которое не является необязательным, то оно по определению не нуждается в развертывании.
Возможно, вы упомянули, что Xcode, по-видимому, реже жалуется, когда вы на самом деле принудительно разворачиваете необязательные параметры, или, как дурная привычка Xcode, предлагает вам принудительно развернуть вещи, чтобы избежать ошибок времени компиляции. Обычно это происходит потому, что Xcode не может знать во время компиляции, что вы только что написали код, который сломает ваше приложение во время выполнения.
Иногда кажется, что Xcode может иметь только одну единственную цель с его «умными подсказками»: а именно, устранять ошибки времени компиляции. Если вы попытаетесь присвоить значение типа String?
(необязательно String
) типу String
, Xcode выдаст сообщение об ошибке компиляции и спросит, не хотите ли вы добавить оператор принудительной распаковки !
. Умный Xcode, говорите? Мех, Xcode хорош для многих вещей, но решение о том, как вы разворачиваете свои опции, пока не является одной из них. Таким образом, даже если Xcode предлагает вам всевозможные вещи: если вы можете использовать необязательную цепочку, сделайте это.
Конечно, могут быть исключения. Для контроллерной части парадигмы проектирования MVC вы обычно используете оператор as!
для «принудительного преобразования» (приведения), при этом Xcode иногда явно указывает вам использовать as!
вместо as
, например. "Вы имели в виду as!
... ?". В этих ситуациях Xcode иногда может фактически знать, что он делает, и делать вывод, что вы пытаетесь привести, например, пользовательский экземпляр класса UIViewController
к типу UIViewController
, то есть к его родительскому классу. Я бы сказал, что это, возможно, один из немногих случаев, когда я использую «принудительный» маркер !
без ухода на второй круг; принудительное преобразование в типы, которые я знаю со 100% уверенностью, что они могут быть приведены.
Но давайте оставим тему преобразования/приведения типов и перейдем к необязательным типам, переносу и необязательным цепочкам в целом.
Как правило, вам следует избегать принудительного развертывания, если только вы явно не знаете, что объект, который вы разворачиваете, не будет равен нулю. Для общих свойств класса, переменных и т. д., учитывая, что вы задаете этот вопрос так, как вы это делаете, я дам вам следующий совет:
Если вы можете использовать условную распаковку (
if-let
,guard-let
, нулевой оператор объединения??
), то не используйте принудительную распаковку (!
).
Ниже следует пример опасностей принудительной распаковки. Вы можете рассматривать первое предложение if
(if arc4random...
) как любой меньший или больший сегмент какой-либо программы, которую вы написали с помощью методов императивного программирования: мы на самом деле не знаем в деталях, каким будет 'name' до момента выполнения, и наш компилятор не может действительно помочь нам здесь.
var name : String?
/* 'name' might or might not have a non-nil
value after this if clause */
if arc4random_uniform(2) < 1 {
name = "David"
}
/* Well-defined: us an if-let clause to try to unwrap your optional */
if let a = name {
print("Hello world "+a)
/* Very well-behaved, we did a safe
unwrap of 'name', if not nil, to constant a */
print("Hello world "+name!)
/* Well... In this scope, we know that name is,
for a fact, not nil. So here, a force unwrap
is ok (but not needed). */
}
let reallyGiveMeThatNameDammit = name!
/* NOT well-defined. We won't spot this at compile time, but
if 'name' is nil at runtime, we'll encounte a runtime error */
Я рекомендую вам прочитать о необязательных цепочках, ключевой теме в Swift.
ты имеешь в виду то самое какао? вы имеете в виду неявно развернутый?
protocol AnyObject { ... }
Протокол, которому неявно соответствуют все классы.
При использовании в качестве конкретного типа все известные методы и свойства @objc доступны как неявно-необязательные необязательные методы и свойства соответственно для каждого экземпляра AnyObject. Например:
class C {
@objc func getCValue() -> Int { return 42 }
}
// If x has a method @objc getValue()->Int, call it and
// return the result. Otherwise, return nil.
func getCValue1(x: AnyObject) -> Int? {
if let f: ()->Int = x.getCValue { // <===
return f()
}
return nil
}
// A more idiomatic implementation using "optional chaining"
func getCValue2(x: AnyObject) -> Int? {
return x.getCValue?() // <===
}
// An implementation that assumes the required method is present
func getCValue3(x: AnyObject) -> Int { // <===
return x.getCValue() // x.getCValue is implicitly unwrapped. // <===
}