本文是翻译原文地址是:http://www.appcoda.com/beginners-guide-optionals-swift/
iOS Swift语言已经发布。我也已经读了Swift的官方向导并在Xcode 6 beta里尝试了这个语言,我已经开始喜欢它的简单和它的语法了。和我的团队一起,我开始学习这个新语言看它和Objective-C的区别,要知道Objective-C是一个已经有30年历史的语言了。我们学习的同时也尽最大的努力来教初学者并帮助社区来轻松学习这个语言。
Optionals总览
我已经在之前的教程里提到过optionals但是没有深入介绍,所以什么是optional?当我们在Swift中申明变量时,它们被设计成默认非optional,换句话说你不得不分配一个非空值给它们。如果你给一个非optional变量分配一个空值编译器会告诉你:“嗨,不能赋一个空值”。
<span style=" font-stretch: normal; font-size: 13px; line-height: 18.2000007629395px; font-family: Monaco, 'Lucida Console', monospace; white-space: nowrap; color: rgb(0, 51, 102);"><strong></strong></span>var message: String = "Swift is awesome!" // OK
message = nil // compile-time error<span style=" font-stretch: normal; font-size: 13px; line-height: 18.2000007629395px; font-family: Monaco, 'Lucida Console', monospace; white-space: nowrap; color: rgb(0, 102, 0);"><em></em></span>
message = nil // compile-time error 当然这错误消息不是十分用户友好:“Could not find an overload for ‘__conversion’ that accepts the supplied arguments”。声明类属性时也有这样的问题,类属性也是被设计为默认非optional的。
class Messenger {
var message1: String = "Swift is awesome!" // OK
var message2: String // compile-time error
}
NSString *message = @"Objective-C will never die!";
message = nil;
class Messenger {
NSString *message1 = @"Objective will never die!";
NSString *message2;
}
class Messenger {
var message1: String = "Swift is awesome!" // OK
var message2: String? // OK
}
你仍然可以给定义成optional的变量赋值,然而如果这个变量像上面代码一样没有赋任何值,它的值被自动定义成nil。
为什么需要Optionals?
Swift被设计成安全的。像苹果宣称的那样,optionals是一个实在例子表明Swift语言是类型安全的。你也可以通过上面的代码看出来Swift的optionals提供了编译时检查这样可以防止一些常见的编程错误在运行时产生。让我们继续通过下面的例子来好好理解optionals的妙处。
考虑下面的Objective-C方法:
- (NSString *)findStockCode:(NSString *)company {
if ([company isEqualToString:@"Apple"]) {
return @"AAPL";
} else if ([company isEqualToString:@"Google"]) {
return @"GOOG";
}
return nil;
}
假设这个方法是定义在了同一类中并且我们这样用:
NSString *stockCode = [self findStockCode:@"Facebook"]; // nil is returned
NSString *text = @"Stock Code - ";
NSString *message = [text stringByAppendingString:stockCode]; // runtime error
NSLog(@"%@", message);
代码不有编译错误但是会有运行时出现异常因为对于Facebook它返回nil。
用Swift的optionals它在编译时就会报错所以就不会有运行时错误。如果用Swift重写上面代码如下:
func findStockCode(company: String) -> String? {
if (company == "Apple") {
return "AAPL"
} else if (company == "Google") {
return "GOOG"
}
return nil
}
var stockCode:String? = findStockCode("Facebook")
let text = "Stock Code - "
let message = text + stockCode // compile-time error
println(message)
从例子你可以看出,Swift的optionals加强了nil值的检查并提供编译时的错误警告给开发者。明显的,用optionals贡献了更好的代码质量。
打开Optionals变量
我们怎么才能使上面代码工作哪?明显的,我们需要测试是否stockCode包含nil值。我们修改成下面这样:var stockCode:String? = findStockCode("Facebook")
let text = "Stock Code - "
if stockCode {
let message = text + stockCode!
println(message)
}
上面例子,在非空检查后我们可以打开“stockCode”optional变量。也就是说在我们用!运算符打开optional变量之前我们一定知道它包含一个非空值。总是推荐在打开一个optional变量之前确认它一定包含一个值。
但是如果我们忘记验证,像下面的代码:
var stockCode:String? = findStockCode("Facebook")
let text = "Stock Code - "
let message = text + stockCode! // runtime error
fatal error: Can’t unwrap Optional.None
Optional变量绑定
除了强制打开,optional绑定是一个简单并且推荐的方法去打开一个optional变量。我们利用optional绑定来检查是否一个optional变量包含值。如果它包含一个值就会打开它并把它赋给一个常量或者变量。
没有比用一个例子来解释optional绑定更好的了。我们将前面的例子转化成用optional绑定实现。
var stockCode:String? = findStockCode("Facebook")
let text = "Stock Code - "
if let tempStockCode = stockCode {
let message = text + tempStockCode
println(message)
}
“if let” (或是“if var”)是optional绑定的两个关键字。用纯英语描述代码可以说“If stockCode contains a value, unwrap it, set its value to tempStockCode and execute the conditional block. Otherwise, just skip it the block” (“如果stockCode包含一个值那么打开它,把值赋给tempStockCode并执行条件代码块,不然就跳过if代码块”)。tempStockCode是一个新常量,我们不再需要用!后缀去访问它。
我们能再进一步简化上面代码通过直接在if语句中执行函数,如下:
let text = "Stock Code - "
if var stockCode = findStockCode("Apple") {
let message = text + stockCode
println(message)
}
这里的stockCode不是一个optional变量,不再需要在if代码块中用!后缀去访问它的值。如果函数返回nil值if语句就不执行。
optional链
在解释optional链之前让我们修改一下原始的例子。我创建一个新类叫stock有code和price两个optional属性。findStockCode函数修改成返回stock类不再是字符串。
class Stock {
var code: String?
var price: Double?
}
func findStockCode(company: String) -> Stock? {
if (company == "Apple") {
let aapl: Stock = Stock()
aapl.code = "AAPL"
aapl.price = 90.32
return aapl
} else if (company == "Google") {
let goog: Stock = Stock()
goog.code = "GOOG"
goog.price = 556.36
return goog
}
return nil
}
我们重写了原始的例子如下。我们首先调用findStockCode函数找到stock对象,接着我们计算100股所需要的总价值。
if let stock = findStockCode("Apple") {
if let sharePrice = stock.price {
let totalCost = sharePrice * 100
println(totalCost)
}
}
由于findStockCode()函数的返回值是optional的,我们用optional绑定去检查是否它包含一个确切值。明显的,stock的price属性也是optional的所以我们用“if let”语句去测试是否stock.price包含一个非空值。
上面代码运行不会有任何错误。我们可以用optional链来简化代码从而替代这种嵌套的“if set”语句。这个特性允许我们用“?.”运算符来链接多个optionals变量在一起。下面是简化版本的代码:
if let sharePrice = findStockCode("Apple")?.price {
let totalCost = sharePrice * 100
println(totalCost)
}
optional链提供一个可选的方法来访问price属性的值。代码现在看起来整洁简单多了。这里我们只是介绍了optional链的一些基本用法。我们可以通过 Apple's Swift guide去了解更多详情。
Swift和Objective-c交互
Swift的optional是十分强大的,尽管你可能要花一些时间去学习使用这个语法。optionals帮助你清晰你代码中的值避免丢失非空检查。
swift是设计成能还Objective-C API交互的。无论何时你需要和UIKit或是别的framework交互,有必然会访问optionals变量。这儿有一些optionals变量你将会访问当实现一个table view:
func numberOfSectionsInTableView(tableView: UITableView?) -> Int {
// Return the number of sections.
return 1
}
func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return recipes.count
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel.text = recipes[indexPath.row]
return cell
}
总结
理解optionals怎么工作是十分重要的,这也是为什么我们贡献了一整篇文章介绍optionals。Swift的optionals允许开发者在编译时间发现潜在的问题,从而避免运行时的意外错误。一旦你掌握了这种语法,你将会感激optionals的美好。