在Swift中向下转换可选内容:as?类型,还是一样!类型?


Answers:


142

实际的区别是:

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类型的可选?似乎第二种方法的作用与第一种方法相同,但是如果向下转换失败,则可能会崩溃。那么为什么要使用它呢?
西坎德(Sikander)

6
是的@Sikander,第一个总是更好。我永远不会使用第二个。
vacawama

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
    }

    因此,让我们像这样记住它-如果?这意味着我们不确定值是否为零(当我们不知道事物时会出现问号)。

  2. 与此相反:

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

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


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

+1为您的示例,但您可以使用与之相同的示例来解释我!代替?同时向下转换为let cell = tableView.dequeueReusableCellWithIdentifier(“ Cell”)as!UITableViewCell ..我猜是吗?足够了,为什么需要as!
阿尼

让cell = tableView.dequeueReusableCellWithIdentifier(“ Cell”)为?UITableViewCell。-在这里,我们不知道将标识符为“ Cell”的单元格向下转换为UITableViewCell的结果是否为零。如果为nill,则返回nill(因此我们避免此处崩溃)。
jishnu bala 2015年

有趣,intNil as! String? // ==nil不引起碰撞!!! ???,作为可选<Int>区域.None是从可选<字符串> .None不同
onmyway133

你为什么垂头丧气as?String?为什么不将其转换为String??你为什么不垂头丧气as!String
亲爱的

试图在Swift 3中创建这个游乐场,但您必须使用Any代替AnyObject
Honey

9
  • as 用于up铸和铸型为桥接型
  • 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


8

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

as?)(称为条件表单)返回您要向下转换到的类型的可选值。

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


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

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

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


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

另外,令z2 = dict [2]为!字符串//“ Yo”(非可选)
Jay



-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"etc。然后“obj.lastName”将其设置为nil值。所以,没有崩溃(快乐:)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.