Swift中的低价选项:as?输入,或者!类型?

时间:2014-09-07 09:07:01

标签: swift optional downcast optional-variables

在Swift中给出以下内容:

var optionalString: String?
let dict = NSDictionary()

以下两个陈述之间的实际区别是什么:

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

VS

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

9 个答案:

答案 0 :(得分:131)

实际差异在于:

var optionalString = dict["SomeKey"] as? String

optionalString将是String?类型的变量。如果基础类型不是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)
}

答案 1 :(得分:11)

为了澄清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

答案 2 :(得分:11)

as? Types - 表示向下投射过程是可选的。该过程可以成功与否(如果向下转换失败,系统将返回nil。)如果向下转换失败,任何方式都不会崩溃。

as! Type? - 这里的强制转换过程应该是成功的(!表示)。结束问号表示最终结果是否为零。

有关“!”的更多信息和“?”

让我们看两个案例

  1. 考虑:

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

    这里我们不知道将具有标识符“Cell”的单元格向下转换为UITableViewCell的结果是否成功。如果不成功则返回nil(因此我们避免崩溃)。在这里我们可以做到如下。

    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. 
    

    这里我们告诉编译器,下击应该是成功的。如果失败,系统将崩溃。因此,当我们确定该值为非零时,我们会给!

答案 3 :(得分:8)

它们是Swift中两种不同形式的向下转换

as?,已知为条件表单,会返回您尝试向下转换的类型的可选值。

  

当您不确定向下转播是否成功时,您可以使用它。   这种形式的运算符将始终返回一个可选值,并且   如果无法做出贬低,价值将为零。这使得   你要检查一个成功的沮丧。

as!,知道为强制表单,尝试向下转换并强制展开结果作为单个复合操作。< / p>

  

当您确定向下转动时,您应该使用   永远成功。这种形式的运算符将触发运行时   错误如果您尝试向下转换为不正确的类类型。

有关详细信息,请查看Apple文档的 Type Casting 部分。

答案 4 :(得分:6)

  • as用于向上转换和类型转换为桥接类型
  • as?用于安全投射,如果失败则返回nil
  • 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属性中。

我确定您已经猜到这正是Optional数据类型的情况。如果检查UIViewController,您将看到该属性定义为:

var navigationController: UINavigationController? { get }

让我们回到我们的用例。如果您知道您的视图控制器将始终具有导航控制器,您可以继续并强行打开它:

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

当你放一个!在你告诉编译器的属性名称后面 我不在乎这个属性是可选的,我知道当这个代码执行时总会有一个值存储,所以请像对待普通的数据类型一样对待这个。 那不是很好吗?如果您的视图控制器没有导航控制器会发生什么?如果你的建议总是会有一个存储在navigationController中的值是错误的?你的应用会崩溃。简单而丑陋。

  

所以,使用!只有当你有101%确定这是安全的时候。

如果您不确定是否会有导航控制器怎么样?那你可以用吗?而不是!:

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

什么?在属性名称后面告诉编译器是 我不知道这个属性是否包含nil或值,所以:如果它有值,则使用它,并且只考虑整个表达式nil。 有效吗?允许您在有导航控制器的情况下使用该属性。如果检查任何种类或任何类型的铸件,请勿。当您不关心是否有导航控制器时,这种语法是完美的,并且只有在存在时才想做某事。

非常感谢Fantageek

答案 5 :(得分:3)

也许这个代码示例可以帮助某些人理解这个原则:

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

答案 6 :(得分:0)

The first is a "conditional cast" (look under "type-casting operators" in the documentation I've linked)。如果转换成功,表达式的值将包装在一个可选项中并返回,否则返回的值为nil。

第二个意味着optionalString可以是一个字符串对象,也可能是nil。

More information found in this related question

答案 7 :(得分:0)

在Swift中记住这些运算符的模式可能最容易:The publisher of this Application could not be verified暗示“这可能陷阱”,而!表示“这可能是零。”

参考: https://developer.apple.com/swift/blog/?id=23

答案 8 :(得分:-1)

我是Swift的新手,写这个例子试图解释,因为我理解'选项'。如果我错了,请纠正我。

感谢。

bool

(1):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)

VS

(2):obj.lastName = obj.lName as! String

答案:(1)程序员确信obj.lastName = obj.lName as? String包含字符串类型对象。所以只需将该值赋予“obj.lName”

现在,如果程序员是正确的意味着“obj.lastName”是字符串类型对象,那么没问题。 “obj.lastName”将设置为相同的值。

但是如果程序员错误意味着"obj.lName"不是字符串类型对象,即它包含一些其他类型的对象,如“NSNumber”等。然后CRASH(运行时错误)。

(2)程序员不确定"obj.lName"是否包含字符串类型对象或任何其他类型对象。因此,如果它是字符串类型,请将该值设置为“obj.lName”

现在,如果程序员是正确的意味着“obj.lastName”是字符串类型对象,那么没问题。 “obj.lName”将设置为相同的值。

但是如果程序员错误意味着obj.lName不是字符串类型对象,即它包含一些其他类型的对象,如“obj.lastName”等。然后"NSNumber"将设置为nil值。所以,没有崩溃(快乐:)