Swift 中的向下转换选项:as?键入,或作为!类型?

Posted

技术标签:

【中文标题】Swift 中的向下转换选项:as?键入,或作为!类型?【英文标题】:Downcasting optionals in Swift: as? Type, or as! Type? 【发布时间】:2014-09-07 09:07:00 【问题描述】:

鉴于 Swift 中的以下内容:

var optionalString: String?
let dict = NSDictionary()

以下两种说法的实际区别是什么:

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

vs

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

【问题讨论】:

另见The as! Operator from Apple 【参考方案1】:

实际区别是这样的:

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)

【讨论】:

第一种方法不是总是更好吗?两者都返回 String 类型的可选项?似乎第二种方法与第一种方法执行相同的操作,但如果向下转换不成功,则可能会崩溃。那么为什么要使用它呢? 是的@Sikander,第一个总是更好。我永远不会使用第二个。【参考方案2】:

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

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

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

让我们拿两个案例

    考虑:

    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(当我们不知道的时候会出现问号)。

    对比:

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

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

【讨论】:

【参考方案3】:

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

【讨论】:

+1 作为你的例子,但你能用同样的例子解释我吗?代替 as?同时向下转换为 let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell ..我猜是?就足够了,为什么需要 as! 让 cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell。 - 这里我们不知道将标识符为“Cell”的单元格向下转换为 UITableViewCell 的结果是否为零。如果为 nill,则返回 nill(因此我们避免在这里崩溃)。 有趣,intNil as! String? // ==nil 不会导致崩溃!!!???,因为 Optional.None 不同于 Optional.None 你为什么将as? 转为String?你为什么不把它贬低到String??为什么不将as! 转为String 尝试在 Swift 3 中做这个游乐场,但你必须使用 Any 而不是 AnyObject【参考方案4】: 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

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

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

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

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

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

var navigationController: UINavigationController?  get 

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

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

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

所以,使用 !仅当您 101% 确定这是安全的。

如果您不确定是否总会有一个导航控制器呢?那你可以用吗?而不是 !:

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

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

非常感谢Fantageek

【讨论】:

【参考方案5】:

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

(as?),已知为 条件形式,返回您尝试向下转换为的类型的可选值。

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


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

您应该在您确定向下转换时使用它 总是成功。这种形式的操作符会触发一个runtime 如果您尝试向下转换为不正确的类类型,则会出现错误

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

【讨论】:

【参考方案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

【讨论】:

另外,设 z2 = dict[2] as! String // “哟”(非可选)【参考方案7】:

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.

【讨论】:

【参考方案8】:

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

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

【讨论】:

【参考方案9】:

我是 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

Ans : (1) 这里的程序员很确定“obj.lName” 包含字符串类型的对象。因此,只需将该值赋予 “obj.lastName”

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

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

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

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

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

【讨论】:

以上是关于Swift 中的向下转换选项:as?键入,或作为!类型?的主要内容,如果未能解决你的问题,请参考以下文章

Swift-类型转换(Type Casting)(十七)

在swift 3中键入任何没有下标错误

什么是 Swift 中的桥接转换,如以下警告所示:来自“数据?”的条件向下转换to 'CKRecordValue 是一种桥接转换

在组合框中使用向下/向上箭头键选择数据而不更新数据,直到点击选项卡或输入 MS Access

40-向下转换 as 定义接口

如何在 for in 循环中向下转换为 [String: String]?