在 Swift 中给定以下内容:
var optionalString: String?
let dict = NSDictionary()
以下两个语句有何实际区别:
optionalString = dict.objectForKey("SomeKey") as? String
对比
optionalString = dict.objectForKey("SomeKey") as! String?
在 Swift 中给定以下内容:
var optionalString: String?
let dict = NSDictionary()
以下两个语句有何实际区别:
optionalString = dict.objectForKey("SomeKey") as? String
对比
optionalString = dict.objectForKey("SomeKey") as! String?
实际上的区别在于:
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)
}
as? Types
- 表示向下转型过程是可选的。该过程可能成功,也可能失败(如果向下转型失败,则系统将返回nil)。无论如何,如果向下转型失败,都不会崩溃。
as! Type?
- 在这里,向下转型的过程应该是成功的(!
表示),结尾的问号表示最终结果是否可以为nil。
关于“!”和“?”的更多信息
我们来看两个例子
考虑:
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
}
那么让我们这样记住 - 如果?
,意味着我们不确定值是否为nil(问号表示当我们不知道事情时)。
相比之下:
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell.
在这里,我们告诉编译器向下转换应该成功。如果失败了,系统将崩溃。因此,当我们确定值为非空时,我们使用 !
。
为了澄清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
intNil as! String? // ==nil
不会导致崩溃!!!???因为 Optional<Int>.None 与 Optional<String>.None 是不同的。 - onmyway133as?
转换成 String
?为什么不将其转换成 String?
?为什么不将 as!
转换成 String
? - mfaaniAny
而不是AnyObject
。 - mfaanias
用于向上转型和类型转换为桥接类型as?
用于安全转换,如果失败则返回nilas!
用于强制转换,如果失败则崩溃注意:
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
var navigationController: UINavigationController? { get }
让我们回到我们的使用案例。如果您确定您的视图控制器始终会有导航控制器,那么可以强制对其进行解包:
controller.navigationController!.pushViewController(myViewController, animated: true)
如果您不确定是否始终会有导航控制器,则可以使用?而不是!:因此,仅在101%确定安全时使用!
controller.navigationController?.pushViewController(myViewController, animated: true)
在Swift中,Downcasting有两种不同的形式。
(as?
),也称为条件形式,返回你试图向下转换的类型的可选值。
当你不确定向下转换是否成功时,可以使用它。这个运算符的这种形式总是返回一个可选值, 如果向下转换不可能,该值将为nil。这使得你可以检查向下转换是否成功。
(as!
),也称为强制形式,尝试进行向下转换,并将结果作为单个复合操作进行强制解包。
只有当你确信向下转换总是成功时才应该使用它。如果尝试向下转换到错误的类类型,则此运算符的这种形式将触发运行时错误。
有关更多详细信息,请查看Apple文档中的Type Casting部分。
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
针对这些运算符,最容易记住的方法可能是在Swift中使用以下模式:!
意味着“这可能会导致错误”,而?
表示“这可能为空值”。
第一个是“条件转换”(在我链接的文档中查找“类型转换运算符”)。如果转换成功,则表达式的值将被包装在可选项中并返回;否则,返回的值为nil。
第二个意味着optionalString可以是字符串对象,也可能是nil。
我是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
vs
(2) : obj.lastName = obj.lName as? String
答案:(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值。因此,没有崩溃(开心:)