Swift Optionals 初学者向导

本文是翻译原文地址是: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
}

你将会得到一个对于message2的编译时错误因为它没有被赋予初值。比较于Objective-C你将会感到一些诧异,因为在Objective-C你将不会得到编译错误当给变量赋空值或是声明一个属性没有初始值。

NSString *message = @"Objective-C will never die!";
message = nil;
class Messenger {
    NSString *message1 = @"Objective will never die!";
    NSString *message2;
}

然而这不是说你不能声明一个没有初始值的类属性在Swift中。Swift引入optional类型来指明缺席值。它是通过在类型定义后加一个问号?来实现的,下面是一个例子:

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;
}

你可以用findStockCode:方法得到特定公司股票代码。由于演示目的,这个方法只是返回苹果和谷歌公司的股票代码,对于别的输入它返回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)

stockCode定义为一个optional变量意味着它可以包含一个字符串也可以是nil值。你不能执行上面的代码因为编译器会报错(“value of optional type String? is not unwrapped)让你来改正这个错误。

从例子你可以看出,Swift的optionals加强了nil值的检查并提供编译时的错误警告给开发者。明显的,用optionals贡献了更好的代码质量。

打开Optionals变量

我们怎么才能使上面代码工作哪?明显的,我们需要测试是否stockCode包含nil值。我们修改成下面这样:

var stockCode:String? = findStockCode("Facebook")
let text = "Stock Code - "
if stockCode {
    let message = text + stockCode!
    println(message)
}

就像Objective-C的对应办法,我们用if语句来判断是否这个optional变量包含一个非空值。一旦我们知道这个optional变量一定包含一个非空值,我们可以通过在optional变量名后加!运算符来打开它。在Swift这被叫做强制打开。你用!运算符来打开一个optional变量找回它的实际值。

上面例子,在非空检查后我们可以打开“stockCode”optional变量。也就是说在我们用!运算符打开optional变量之前我们一定知道它包含一个非空值。总是推荐在打开一个optional变量之前确认它一定包含一个值。

但是如果我们忘记验证,像下面的代码:

var stockCode:String? = findStockCode("Facebook")
let text = "Stock Code - "
let message = text + stockCode!  // runtime error

这时没有编译错误。编译器假设optional变量包含一个值因为这里使用了强制打开运算符。当运行这个app时一个下面的运行时错误将会抛出。

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的美好。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值