Подтвердить что ты не робот

Опционы Downcasting в Swift: как? Тип, или как! Тип?

В Swift указано следующее:

var optionalString: String?
let dict = NSDictionary()

Какова практическая разница между двумя следующими утверждениями:

optionalString = dict.objectForKey("SomeKey") as? String

против

optionalString = dict.objectForKey("SomeKey") as! String?
4b9b3361

Ответ 1

Практическое различие заключается в следующем:

var optionalString = dict.objectForKey("SomeKey") as? String

optionalString будет переменной типа String?. Если базовый тип - это нечто иное, чем String, это безвредно назначает nil необязательному.

var optionalString = dict.objectForKey("SomeKey") as! String?

Это говорит, я знаю, что это вещь String?. Это также приведет к тому, что optionalString будет иметь тип String?, , но он сработает, если базовый тип - это что-то еще.

Первый стиль затем используется с if let для безопасного разворачивания необязательного:

if let string = dict.objectForKey("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".
    println(string)
}

Ответ 2

Чтобы уточнить, что сказал vacawama, вот пример...

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

Ответ 3

как? Тип - означает, что процесс литья вниз не является обязательным. Процесс может быть успешным или нет (система вернет нуль, если сбрасывание вниз закончится). Любой способ не сбой, если сбрасывание вниз происходит.

как! Тип? - Здесь процесс down casting должен быть успешным ( "!" Указывает это). Конечный знак вопроса указывает, может ли конечный результат быть нулевым или нет.

Дополнительная информация о "!" и "?"

Возьмем 2 случая

1) let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell.
- здесь мы не знаем, удастся ли результат сбрасывания ячейки с идентификатором "Ячейка" в UITableViewCell или нет. Если это неуспешно, тогда он возвращает nil (поэтому мы избегаем краха здесь). Здесь мы можем сделать, как показано ниже.

if let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell {
//If we reached here it means the down casting was successfull
}
else {
// unsuccessful down casting
}

Итак, давайте вспомним это так: "Если"? это означает, что мы не уверены, является ли значение нулем или нет (знак вопроса приходит, когда мы не знаем ничего).

2)

 let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell. 
  • здесь мы говорим компилятору, что приведение в литье должно быть успешным. В случае сбоя система выйдет из строя. Итак, мы даем "!" когда мы уверены, что значение не равно нулю.

Ответ 4

Это две разные формы Downcasting в Swift.

(as?), который, как известно, является Условной формой, возвращает необязательное значение типа, с которым вы пытаетесь сбрасывать.

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


(as!), который, как известно, является Принудительной формой, пытается опустошить и принудительно развернуть результат как одно составное действие.

Вы должны использовать его ТОЛЬКО, когда вы уверены, что всегда преуспевает. Эта форма оператора вызовет время выполнения ошибка, если вы попытаетесь перетащить в неправильный тип класса.

Для получения дополнительной информации, пожалуйста, проверьте Тип Casting в документации Apple.

Ответ 5

  • as, используемый для повышения уровня и типа литья для мостового типа
  • as? используется для безопасного литья, возвращает nil, если не удалось
  • as! используется для принудительного каста, сбой при неудаче

Примечание:

  • as! Невозможно применить тип raw к необязательному

Примеры:

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, было бы неправильно? Ваше приложение выйдет из строя. Простой и уродливый.

Итак, используйте! только если вы на 101% уверены, что это безопасно.

Как насчет уверенности в том, что всегда будет контроллер навигации? Тогда вы можете использовать? вместо a!:

controller.navigationController?.pushViewController(myViewController, animated: true)

Что? за именем свойства говорит, что компилятор Я не знаю, содержит ли это свойство ниль или значение, поэтому: если оно имеет значение, используйте его, а иначе просто рассмотрите все выражение nil. Эффективно? позволяет использовать это свойство только в том случае, если есть контроллер навигации. Нет, если какие-либо проверки или кастинги любого рода. Этот синтаксис идеально подходит, когда вам не важно, есть ли у вас контроллер навигации или нет, и хотите что-то сделать, только если есть.

Огромное спасибо Fantageek

Ответ 6

Возможно, этот пример кода поможет кому-то понять принцип:

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

Ответ 7

Первый - это условный отбор (смотрите ссылку "Операторы литья типа" в связанной документации). Если листинг преуспевает, значение выражения заверяется в необязательный и возвращенный, в противном случае возвращаемое значение равно nil.

Второе означает, что optionalString может быть строковым объектом или может быть nil.

Дополнительная информация содержится в этом связанном вопросе.

Ответ 8

Проще всего вспомнить шаблон для этих операторов в Swift, поскольку: ! подразумевает, что "это может быть ловушка", в то время как ? указывает "это может быть ноль".

относятся к: https://developer.apple.com/swift/blog/?id=23

Ответ 9

Я начинаю с Swift и пишу этот пример, пытаясь объяснить, как я понимаю о "optionals". Если я ошибаюсь, исправьте меня.

Спасибо.


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

Ans: (1) Здесь программист уверен, что "obj.lName" содержит объект типа строки. Поэтому просто дайте это значение "obj.lastName".

Теперь, если программист правильно, значит, "obj.lName" является объектом типа string, а затем проблем нет. "obj.lastName" будет установлено одинаковое значение.

Но если программист ошибочен, значит, "obj.lName" не является объектом типа string, то есть содержит другой объект типа типа NSNumber и т.д. Затем CRASH (ошибка времени выполнения).

(2) Программист не уверен, что "obj.lName" содержит объект типа строки или любой другой объект типа. Поэтому установите это значение в "obj.lastName", если оно является строковым типом.

Теперь, если программист правильно, значит, "obj.lName" является объектом типа string, а затем проблем нет. "obj.lastName" будет установлено одинаковое значение.

Но если программист ошибочен, obj.lName не является объектом типа string, то есть содержит другой объект типа типа "NSNumber" и т.д. Затем "obj.lastName" будет устанавливать значение nil. Итак, No Crash (Happy:)