99

鉴于 Swift 中的以下内容:

var optionalString: String?
let dict = NSDictionary()

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

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

对比

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

9 回答 9

150

实际的区别是:

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

optionalString将是一个类型的变量String?。如果基础类型不是 aString这将无害地分配nil给可选项。

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

这就是说,我知道这东西是一个String?. 这也将导致optionalStringtype 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)
}
于 2014-09-07T12:28:37.057 回答
14

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

as! Type?- 这里的下铸过程应该是成功的(!表示)。结尾问号表示最终结果是否可以为零。

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

让我们拿2个案例

  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
    }
    

    所以让我们像这样记住它 - 如果?这意味着我们不确定 value 是否为 nil (问号出现在我们不知道的情况下)。

  2. 与之对比:

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

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

于 2015-06-04T17:21:14.860 回答
11

为了澄清 vacawama 所说的,这里有一个例子......

斯威夫特 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

斯威夫特 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
于 2015-03-27T18:10:50.773 回答
9
  • 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

通过添加? 在数据类型之后立即告诉编译器该变量可能包含或不包含数字。整洁的!请注意,定义可选常量并没有真正意义——您只能设置一次它们的值,因此您可以判断它们的值是否为 nil。

什么时候应该使用“?” 什么时候 ”!”

假设我们有基于 UIKit 的简单应用程序。我们的视图控制器中有一些代码,并希望在其之上呈现一个新的视图控制器。我们需要决定使用导航控制器将新视图推送到屏幕上。

我们知道每个 ViewController 实例都有一个属性导航控制器。如果您正在构建基于导航控制器的应用程序,则应用程序主视图控制器的此属性会自动设置,您可以使用它来推送或弹出视图控制器。如果您使用单个应用程序项目模板 - 不会为您自动创建导航控制器,因此您的应用程序的默认视图控制器将不会在 navigationController 属性中存储任何内容。

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

var navigationController: UINavigationController? { get }

所以让我们回到我们的用例。如果你知道你的视图控制器总是有一个导航控制器,你可以继续并强制打开它:

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

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

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

如果你不确定总是会有一个导航控制器怎么办?那你可以用吗?代替 !:

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

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

非常感谢Fantageek

于 2016-03-31T20:20:46.307 回答
8

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

( as?),它被称为Conditional Form,它返回您尝试向下转换为的类型的可选值。

当您不确定向下转换是否会成功时,您可以使用它。这种形式的运算符将始终返回一个可选值,如果无法进行向下转换,则该值将为 nil。这使您能够检查是否成功向下转换。


( as!),它被称为强制形式,尝试向下转换并将结果强制展开为单个复合动作。

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

有关更多详细信息,请查看Apple 文档的类型转换部分。

于 2015-07-08T10:20:23.407 回答
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
于 2015-07-03T18:54:25.170 回答
0

第一个是“条件转换”(在我链接的文档中的“类型转换运算符”下查看)。如果转换成功,则表达式的值被包装在一个可选项中并返回,否则返回的值为 nil。

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

在这个相关问题中找到更多信息

于 2014-09-07T09:14:36.273 回答
0

记住 Swift 中这些运算符的模式可能是最容易的:!暗示“这可能会陷入陷阱”,而?表示“这可能是 nil”。

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

于 2016-03-04T14:06:28.020 回答
-1
于 2016-09-02T10:57:50.177 回答