Опции понижающего преобразования в Swift: как? Типа, или как! Тип?

96

Учитывая следующее в Swift:

var optionalString: String?
let dict = NSDictionary()

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

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

против

optionalString = dict.objectForKey("SomeKey") as! String?
sdduursma
источник
1
Также см . Как! Оператор от Apple
Honey

Ответы:

143

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

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)
}
Вачавама
источник
Разве тогда не всегда лучше первый метод? Оба возвращают необязательный тип String? Кажется, что второй метод делает то же самое, что и первый, но может привести к сбою, если понижающее преобразование не будет успешным. Так зачем вообще его использовать?
Сикандер
6
Да @ Сикандер, первый всегда лучше. Я бы никогда не использовал второй.
vacawama
14

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

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

Подробнее о "!" а также "?"

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

  1. Рассмотреть возможность:

    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
    }
    

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

  2. Сравните это с:

    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell. 
    

    Здесь мы говорим компилятору, что приведение вниз должно быть успешным. В случае неудачи система выйдет из строя. Поэтому мы даем, !когда уверены, что значение не равно нулю.

Джишну Бала
источник
11

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

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
Чувственный
источник
+1 для вашего примера, но можете ли вы объяснить мне тот же пример для использования! вместо as? при понижении значения как let cell = tableView.dequeueReusableCellWithIdentifier ("Cell") как! UITableViewCell..гадать как? было достаточно, почему была необходимость as!
Аниш Параджули 웃
let cell = tableView.dequeueReusableCellWithIdentifier ("Cell") как? UITableViewCell. - здесь мы не знаем, является ли результат down casting ячейки с идентификатором "Cell" в UITableViewCell нулевым или нет. Если nill, то он возвращает nill (чтобы избежать сбоев здесь).
jishnu bala
интересно, intNil as! String? // ==nilне вызывает сбоя !!! ???, как Optional <Int> .None не отличается от Optional <String> .None
onmyway133 01
почему вы опущенный as?к String? Почему бы тебе не опустить это до String?? Почему вы не опущенными as!к String?
Хани
Попытка сделать эту игровую площадку в Swift 3, но вы должны использовать AnyвместоAnyObject
Дорогая,
9
  • 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 всегда будет храниться значение, было ошибкой? Ваше приложение выйдет из строя. Вот так просто и безобразно.

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

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

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

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

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

SwiftBoy
источник
8

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

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

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


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

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

Дополнительные сведения см. В разделе « Приведение типов» документации Apple.

Скотт Чжу
источник
4

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

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
smileBot
источник
Также пусть z2 = dict [2] as! Строка // «Йо» (необязательно)
Джей,
0

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

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

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

Михаэль Даутерманн
источник
0

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

обратитесь к: https://developer.apple.com/swift/blog/?id=23

Zgpeace
источник
-1

Я новичок в 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 :)

iPhoneBuddy
источник