Учитывая следующее в Swift:
var optionalString: String?
let dict = NSDictionary()
В чем практическая разница между следующими двумя утверждениями:
optionalString = dict.objectForKey("SomeKey") as? String
против
optionalString = dict.objectForKey("SomeKey") as! String?
swift
optional
downcast
optional-variables
sdduursma
источник
источник
Ответы:
Практическая разница заключается в следующем:
var optionalString = dict["SomeKey"] as? String
optionalString
будет переменной типаString?
. Если базовым типом является нечто иное, чем a,String
это будет безвредно просто присвоеноnil
необязательному.var optionalString = dict["SomeKey"] as! String?
Это говорит о том, что я знаю, что это файл
String?
. Это тоже приведет кoptionalString
томуString?
, что будет тип , но он выйдет из строя, если базовый тип будет чем-то другим.Затем используется первый стиль,
if let
чтобы безопасно развернуть необязательный:if let string = dict["SomeKey"] as? String { // If I get here, I know that "SomeKey" is a valid key in the dictionary, I correctly // identified the type as String, and the value is now unwrapped and ready to use. In // this case "string" has the type "String". print(string) }
источник
as? Types
- означает, что процесс литья вниз не является обязательным. Процесс может быть успешным или нет (система вернет nil, если приведение вниз не удастся). В любом случае не произойдет сбой, если приведение вниз не удастся.as! Type?
- Здесь процесс разливки должен пройти успешно (!
указывает на это). Конечный вопросительный знак указывает, может ли окончательный результат быть нулевым или нет.Подробнее о "!" а также "?"
Возьмем 2 случая
Рассмотреть возможность:
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell
Здесь мы не знаем, является ли результат преобразования ячейки с идентификатором «Cell» в UITableViewCell успешным или нет. В случае неудачи возвращается ноль (чтобы избежать сбоев здесь). Здесь мы можем сделать, как указано ниже.
if let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell { // If we reached here it means the down casting was successful } else { // unsuccessful down casting }
Итак, давайте запомним это так: если
?
это означает, что мы не уверены, равно ли значение нулю (знак вопроса появляется, когда мы чего-то не знаем).Сравните это с:
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell.
Здесь мы говорим компилятору, что приведение вниз должно быть успешным. В случае неудачи система выйдет из строя. Поэтому мы даем,
!
когда уверены, что значение не равно нулю.источник
Чтобы прояснить, что сказал Вачавама, вот пример ...
Swift 3.0:
import UIKit let str_value: Any = String("abc")! let strOpt_value: Any? = String("abc")! let strOpt_nil: Any? = (nil as String?) let int_value: Any = Int(1) let intOpt_value: Any? = Int(1) let intOpt_nil: Any? = (nil as Int?) // as String //str_value as String // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast? //strOpt_value as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast? //strOpt_nil as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast? //int_value as String // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast? //intOpt_value as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast? //intOpt_nil as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast? // as? String str_value as? String // == "abc" strOpt_value as? String // == "abc" strOpt_nil as? String // == nil int_value as? String // == nil intOpt_value as? String // == nil intOpt_nil as? String // == nil // as! String str_value as! String // == "abc" strOpt_value as! String // == "abc" //strOpt_nil as! String // Run-Time Error: unexpectedly found nil while unwrapping an Optional value. //int_value as! String // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'. //intOpt_value as! String // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'. //intOpt_nil as! String // Run-Time Error: unexpectedly found nil while unwrapping an Optional value. // as String? //str_value as String? // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion //strOpt_value as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast? //strOpt_nil as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast? //int_value as String? // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion //intOpt_value as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast? //intOpt_nil as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast? // as? String? //str_value as? String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?' strOpt_value as? String? // == "abc" strOpt_nil as? String? // == nil //int_value as? String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?' intOpt_value as? String? // == nil intOpt_nil as? String? // == nil // as! String? //str_value as! String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?' strOpt_value as! String? // == "abc" strOpt_nil as! String? // == nil //int_value as! String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?' //intOpt_value as! String? // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'. intOpt_nil as! String? // == nil // let _ = ... as String //if let _ = str_value as String { true } // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast? //if let _ = strOpt_value as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast? //if let _ = strOpt_nil as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast? //if let _ = int_value as String { true } // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast? //if let _ = intOpt_value as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast? //if let _ = intOpt_nil as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast? // let _ = ... as? String if let _ = str_value as? String { true } // true if let _ = strOpt_value as? String { true } // true if let _ = strOpt_nil as? String { true } // false if let _ = int_value as? String { true } // false if let _ = intOpt_value as? String { true } // false if let _ = intOpt_nil as? String { true } // false // let _ = ... as! String //if let _ = str_value as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String' //if let _ = strOpt_value as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String' //if let _ = strOpt_nil as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String' //if let _ = int_value as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String' //if let _ = intOpt_value as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String' //if let _ = intOpt_nil as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String' // let _ = ... as String? //if let _ = str_value as String? { true } // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion //if let _ = strOpt_value as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast? //if let _ = strOpt_nil as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast? //if let _ = int_value as String? { true } // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion //if let _ = intOpt_value as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast? //if let _ = intOpt_nil as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast? // let _ = ... as? String? //if let _ = str_value as? String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?' if let _ = strOpt_value as? String? { true } // true if let _ = strOpt_nil as? String? { true } // true //if let _ = int_value as? String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?' if let _ = intOpt_value as? String? { true } // false if let _ = intOpt_nil as? String? { true } // true // let _ = ... as! String? //if let _ = str_value as! String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?' if let _ = strOpt_value as! String? { true } // true if let _ = strOpt_nil as! String? { true } // false //if let _ = int_value as! String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?' //if let _ = intOpt_value as! String? { true } // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'. if let _ = intOpt_nil as! String? { true } // false
Swift 2.0:
import UIKit let str: AnyObject = String("abc") let strOpt: AnyObject? = String("abc") let strNil: AnyObject? = (nil as String?) let int: AnyObject = Int(1) let intOpt: AnyObject? = Int(1) let intNil: AnyObject? = (nil as Int?) str as? String // == "abc" strOpt as? String // == "abc" strNil as? String // == nil int as? String // == nil intOpt as? String // == nil intNil as? String // == nil str as! String? // Compile-Time Error: Cannot downcast from 'AnyObject' to a more optional type 'String?' strOpt as! String? // == "abc" strNil as! String? // == nil int as! String? // Compile-Time Error: Cannot downcast from 'AnyObject' to a more optional type 'String?' intOpt as! String? // Run-Time Error: Could not cast value of type '__NSCFNumber' to 'NSString' intNil as! String? // == nil
источник
intNil as! String? // ==nil
не вызывает сбоя !!! ???, как Optional <Int> .None не отличается от Optional <String> .Noneas?
кString
? Почему бы тебе не опустить это доString?
? Почему вы не опущеннымиas!
кString
?Any
вместоAnyObject
as
используется для преобразования и преобразования типа в мостовой типas?
используется для безопасного литья, в случае неудачи вернуть нольas!
используется для принудительного литья, сбой в случае неудачиЗаметка:
as!
не может привести исходный тип к необязательномуПримеры:
let rawString: AnyObject = "I love swift" let optionalString: AnyObject? = "we love swift" let nilString: AnyObject? = (nil as String?) let rawInt: AnyObject = Int(3) let optionalInt: AnyObject? = Int(3) let nilInt: AnyObject? = (nil as Int?)
пример
var age: Int? = nil var height: Int? = 180
Добавив ? сразу после типа данных вы сообщаете компилятору, что переменная может содержать число или нет. Аккуратно! Обратите внимание, что на самом деле нет смысла определять необязательные константы - вы можете установить их значение только один раз, и поэтому вы сможете сказать, будет ли их значение нулем или нет.
Когда мы должны использовать "?" и когда "!"
допустим, у нас есть простое приложение на основе UIKit. у нас есть некоторый код в нашем контроллере представления, и мы хотим представить новый контроллер представления поверх него. и нам нужно решить вывести новое представление на экран с помощью контроллера навигации.
Как мы знаем, каждый экземпляр ViewController имеет контроллер навигации по свойствам. Если вы создаете приложение на основе контроллера навигации, это свойство главного контроллера представления вашего приложения устанавливается автоматически, и вы можете использовать его для отправки или вывода контроллеров представления. Если вы используете один шаблон проекта приложения - для вас не будет автоматически создан контроллер навигации, поэтому в контроллере представления по умолчанию вашего приложения не будет ничего, хранящегося в свойстве navigationController.
Я уверен, что вы уже догадались, что это как раз тот случай, когда используется дополнительный тип данных. Если вы проверите UIViewController, вы увидите, что свойство определяется как:
var navigationController: UINavigationController? { get }
Итак, вернемся к нашему варианту использования. Если вы точно знаете, что ваш контроллер представления всегда будет иметь контроллер навигации, вы можете развернуть его принудительно:
controller.navigationController!.pushViewController(myViewController, animated: true)
Когда вы ставите! за именем свойства вы говорите компилятору, что меня не волнует, что это свойство является необязательным, я знаю, что при выполнении этого кода всегда будет хранилище значений, поэтому относитесь к нему как к обычному типу данных. Ну разве это не мило? Но что произойдет, если к вашему контроллеру представления не будет контроллера навигации? Если вы предположили, что в navigationController всегда будет храниться значение, было ошибкой? Ваше приложение выйдет из строя. Вот так просто и безобразно.
А если вы не уверены, что контроллер навигации всегда будет? Тогда можно использовать? вместо!:
controller.navigationController?.pushViewController(myViewController, animated: true)
Что за ? за именем свойства сообщает компилятору, что я не знаю, содержит ли это свойство nil или значение, поэтому: если оно имеет значение, используйте его, а в противном случае просто рассмотрите все выражение nil. Эффективно? позволяет использовать это свойство только в случае наличия контроллера навигации. Никаких проверок или отливок любого рода. Этот синтаксис идеален, когда вам все равно, есть ли у вас контроллер навигации или нет, и вы хотите что-то делать, только если он есть.
Огромное спасибо Fantageek
источник
Это две разные формы Downcasting в Swift.
(
as?
) , которая, как известно, является условной формой , возвращает необязательное значение того типа , к которому вы пытаетесь преобразовать.(
as!
) , которая известна как принудительная форма , пытается выполнить приведение вниз и принудительно разворачивает результат как одно сложное действие.Дополнительные сведения см. В разделе « Приведение типов» документации Apple.
источник
Возможно, этот пример кода поможет кому-нибудь понять принцип:
var dict = [Int:Any]() dict[1] = 15 let x = dict[1] as? String print(x) // nil because dict[1] is an Int dict[2] = "Yo" let z = dict[2] as! String? print(z) // optional("Yo") let zz = dict[1] as! String // crashes because a forced downcast fails let m = dict[3] as! String? print(m) // nil. the forced downcast succeeds, but dict[3] has no value
источник
Первый - это «условное приведение» (см. Раздел «Операторы преобразования типов» в документации, на которую я ссылаюсь) . Если приведение завершается успешно, значение выражения помещается в необязательный и возвращается, в противном случае возвращаемое значение равно нулю.
Второй означает, что optionalString может быть строковым объектом или иметь значение nil.
Дополнительную информацию можно найти в этом связанном вопросе .
источник
Проще всего запомнить шаблон для этих операторов в Swift:
!
подразумевает «это может привести к перехвату», а?
указывает «это может быть ноль».обратитесь к: https://developer.apple.com/swift/blog/?id=23
источник
Я новичок в Swift и пишу этот пример, пытаясь объяснить, как я понимаю, о «опциях». Если я ошибаюсь, поправьте меня.
Спасибо.
class Optional { var lName:AnyObject! = "1" var lastName:String! } let obj = Optional() print(obj.lName) print(obj.lName!) obj.lastName = obj.lName as? String print(obj.lastName)
(1):
obj.lastName = obj.lName as! String
против
(2):
obj.lastName = obj.lName as? String
Ответ: (1) Здесь программист уверен, что он
“obj.lName”
содержит объект строкового типа. Так что просто придайте этому значение“obj.lastName”
.Теперь, если программист прав, значит,
"obj.lName"
это объект строкового типа, тогда никаких проблем. "obj.lastName" будет иметь то же значение.Но если программист ошибается, это означает, что
"obj.lName"
это не объект строкового типа, т.е. он содержит объект другого типа, например "NSNumber" и т. Д. Затем CRASH (ошибка времени выполнения).(2) Программист не уверен, что он
“obj.lName”
содержит объект строкового или любого другого типа. Поэтому установите это значение,“obj.lastName”
если это строковый тип.Теперь, если программист прав, значит,
“obj.lName”
это объект строкового типа, тогда никаких проблем.“obj.lastName”
будет установлено то же значение.Но если программист ошибается, это означает, что obj.lName не является объектом строкового типа, т.е. он содержит объект другого типа, например,
"NSNumber"
и т.д. Затем“obj.lastName”
будет установлено значение nil. Итак, без сбоев (Happy :)источник