鍍金池/ 教程/ iOS/ Swift 方法的多面性
與四軸無人機的通訊
在沙盒中編寫腳本
結構體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學
NSString 與 Unicode
代碼簽名探析
測試
架構
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅動開發(fā)
Collection View 動畫
截圖測試
MVVM 介紹
使 Mac 應用數據腳本化
一個完整的 Core Data 應用
插件
字符串
為 iOS 建立 Travis CI
先進的自動布局工具箱
動畫
為 iOS 7 重新設計 App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網絡應用實例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動畫解釋
響應式 Android 應用
初識 TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調試
項目介紹
Swift 的強大之處
測試并發(fā)程序
Android 通知中心
調試:案例學習
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學習的一代人
視頻
Playground 快速原型制作
Omni 內部
同步數據
設計優(yōu)雅的移動游戲
繪制像素到屏幕上
相機與照片
音頻 API 一覽
交互式動畫
常見的后臺實踐
糟糕的測試
避免濫用單例
數據模型和模型對象
Core Data
字符串本地化
View Controller 轉場
照片框架
響應式視圖
Square Register 中的擴張
DTrace
基礎集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點互聯
iCloud 和 Core Data
Views
虛擬音域 - 聲音設計的藝術
導航應用
線程安全類的設計
置換測試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無人機項目
Mach-O 可執(zhí)行文件
UI 測試
值對象
活動追蹤
依賴注入
Swift
項目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數式 API
iOS 7 的多任務
自定義 Collection View 布局
測試 View Controllers
訪談
收據驗證
數據同步
自定義 ViewController 容器轉場
游戲
調試核對清單
View Controller 容器
學無止境
XCTest 測試實戰(zhàn)
iOS 7
Layer 中自定義屬性的動畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲
代碼審查的藝術:Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴展
理解 Scroll Views
使用 VIPER 構建 iOS 應用
Android 中的 SQLite 數據庫支持
Fetch 請求
導入大數據集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機捕捉
語言標簽
同步案例學習
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識別
玩轉字符串
相機工作原理
Build 過程

Swift 方法的多面性

雖然 Objective-C 的語法相對于其他編程語言來說寫法有點奇怪,但是當你真正使用的時候它的語法還是相當的簡單。下面有一些例子:

+ (void)mySimpleMethod
{
    // 類方法
    // 無參數
    // 無返回值
}

- (NSString *)myMethodNameWithParameter1:(NSString *)param1 parameter2:(NSNumber *)param2
{
    // 實例方法
    // 其中一個參數是 NSString 指針類型,另一個是 NSNumber 指針類型
    // 必須返回一個 NSString 指針類型的值
    return @"hello, world!";
}

相比而言,雖然 Swift 的語法看起來與其他編程語言有更多相似的地方,但是它也可以比 Objective-C 更加復雜和令人費解。

在繼續(xù)之前,我需要澄清 Swift 中方法函數之間的不同,因為在本文中我們將使用這兩個術語。按照 Apple 的 Swift Programming Language Book 里面的方法定義:

方法是與某些特定類型相關聯的函數。類、結構體、枚舉都可以定義實例方法;實例方法為給定類型的實例封裝了具體的任務與功能。類、結構體、枚舉也可以定義類型方法,類型方法與類型本身相關聯。類型方法與 Objective-C 中的類方法(class methods)相似。

以上是長文慎讀。一句話:函數是獨立的,而方法是函數封裝在類,結構或者枚舉中的函數。

剖析 Swift 函數

讓我們從簡單的 “Hello,World” Swift 函數開始:

func mySimpleFunction() {
    println("hello, world!")
}

如果你曾在 Objective-C 之外的語言進行過編程,上面的這個函數你會非常熟悉

  • func 表示這是一個函數。
  • 函數的名稱是 mySimpleFunction。
  • 這個函數沒有參數傳入 - 因此是( )。
  • 函數沒有返回值
  • 函數是在{ }中執(zhí)行

現在讓我們看一個稍稍復雜的例子:

func myFunctionName(param1: String, param2: Int) -> String {
    return "hello, world!"
}

這個函數有一個 String 類型且名為 param1 的參數和一個 Int 類型名為 param2 的參數并且返回值是 `String 類型。

調用所有函數

Swift 和 Objective-C 之間其中一個巨大的差別就是當 Swift 函數被調用的時候參數工作方式。如果你像我一樣喜歡 Objective-C 超長的命名方式,那么請記住,在默認情況下 Swift 函數被調用時參數名是不被外部調用包含在內的。

func hello(name: String) {
    println("hello \(name)")
}

hello("Mr. Roboto")

在你增加更多參數到函數之前,一切看起來不是那么糟糕。但是:

func hello(name: String, age: Int, location: String) {
    println("Hello \(name). I live in \(location) too. When is your \(age + 1)th birthday?")
}

hello("Mr. Roboto", 5, "San Francisco")

如果僅閱讀 hello("Mr. Roboto", 5, "San Francisco"),你可能很難知道每一個參數代表什么。

在 Swift 中,有一個概念稱為 *外部參數名稱 * 用來解決這個困惑:

func hello(fromName name: String) {
    println("\(name) says hello to you!")
}

hello(fromName: "Mr. Roboto")

上面函數中,fromName 是一個外部參數,在函數被調用的時候將被包括在調用中。在函數內執(zhí)行時,使用 name 這個內部參數來對輸入進行引用。

如果你希望外部參數和內部參數相同,你不需要寫兩次參數名:

func hello(name name: String) {
    println("hello \(name)")
}

hello(name: "Robot")

只需要在參數前面添加 # 的快捷方式:

func hello(#name: String) {
    println("hello \(name)")
}

hello(name: "Robot")

當然,對于方法而言參數的工作方式略有不同...

調用方法

當被封裝在類 (或者結構,枚舉) 中時,方法的第一個參數名被外部包含,同時所有的后面的參數在方法調用時候被外部包含:

class MyFunClass {

    func hello(name: String, age: Int, location: String) {
        println("Hello \(name). I live in \(location) too. When is your \(age + 1)th birthday?")
    }

}

let myFunClass = MyFunClass()
myFunClass.hello("Mr. Roboto", age: 5, location: "San Francisco")

因此最佳實踐是在方法名里包含第一個參數名,就像 Objective-C 那樣:

class MyFunClass {

    func helloWithName(name: String, age: Int, location: String) {
        println("Hello \(name). I live in \(location) too. When is your \(age + 1)th birthday?")
    }

}

let myFunClass = MyFunClass()
myFunClass.helloWithName("Mr. Roboto", age: 5, location: "San Francisco")

相對于調用函數 “hello”,我將其重命名為 helloWithName,這使得第一個參數 name 變得很清晰。

如果出于一些原因你希望在函數中跳過外部參數名 (我建議如果要這么做的話,你需要一個非常好的理由),為外部函數添加 _ 來解決:

class MyFunClass {

    func helloWithName(name: String, _ age: Int, _ location: String) {
        println("Hello \(name). I live in \(location) too. When is your \(age + 1)th birthday?")
    }

}

let myFunClass = MyFunClass()
myFunClass.helloWithName("Mr. Roboto", 5, "San Francisco")

實例方法是柯里化 (currying) 函數

需要注意一個非??岬氖?Swift 中實例方法是柯里化函數。

柯里化背后的基本想法是函數可以局部應用,意思是一些參數值可以在函數調用之前被指定或者綁定。這個部分函數的調用會返回一個新的函數。

如果我有一個類:

class MyHelloWorldClass {

    func helloWithName(name: String) -> String {
        return "hello, \(name)"
    }
}

我可以建立一個變量指向類中的 helloWithName 函數:

let helloWithNameFunc = MyHelloWorldClass.helloWithName
// MyHelloWorldClass -> (String) -> String

我新的 helloWithNameFuncMyHelloWorldClass -> (String) -> String 類型,這個函數接受我的類的實例并返回另一個函數。新函數接受一個字符串值,并返回一個字符串值。

所以實際上我可以這樣調用我的函數:

let myHelloWorldClassInstance = MyHelloWorldClass()

helloWithNameFunc(myHelloWorldClassInstance)("Mr. Roboto") 
// hello, Mr. Roboto

初始化:一個特殊注意的地方

在類,結構體或者枚舉初始化的時候將調用一個特殊的 init 方法。在 Swift 中你可以像其他方法那樣定義初始化參數:

class Person {

    init(name: String) {
        // your init implementation
        // 你的初始化方法實現
    }

}

Person(name: "Mr. Roboto")

注意下,不像其他方法,初始化方法的第一個參數必須在實例時必須是外部的。

大多數情況下的最佳實踐是添加一個不同的外部參數名 — 本例中的 fromName —讓初始化更具有可讀性:

class Person {

    init(fromName name: String) {
        // your init implementation
        // 你的初始化方法實現
    }

}

Person(fromName: "Mr. Roboto")

當然,就像其他方法那樣,如果你想讓方法跳過外部參數名的話,可以添加 _。我喜歡 Swift Programming Language Book 初始化例子的強大和可讀性。

struct Celsius {
    var temperatureInCelsius: Double
    init(fromFahrenheit fahrenheit: Double) {
        temperatureInCelsius = (fahrenheit - 32.0) / 1.8
    }
    init(fromKelvin kelvin: Double) {
        temperatureInCelsius = kelvin - 273.15
    }
    init(_ celsius: Double) {
        temperatureInCelsius = celsius
    }
}

let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
// boilingPointOfWater.temperatureInCelsius 是 100.0

let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius 是 0.0

let bodyTemperature = Celsius(37.0)
// bodyTemperature.temperatureInCelsius 是 37.0 

如果你希望抽象類/枚舉/結構體的初始化,跳過外部參數可以非常有用。我喜歡在 David Owenjson-swift library 中對這項技術的使用:

public struct JSValue : Equatable {

    // ... 截斷的部分代碼

    /// 使用 `JSArrayType` 來初始化 `JSValue`。 
    public init(_ value: JSArrayType) {
        self.value = JSBackingValue.JSArray(value)
    }

    /// 使用 `JSObjectType` 來初始化 `JSValue`。 
    public init(_ value: JSObjectType) {
        self.value = JSBackingValue.JSObject(value)
    }

    /// 使用 `JSStringType` 來初始化 `JSValue`。 
    public init(_ value: JSStringType) {
        self.value = JSBackingValue.JSString(value)
    }

    /// 使用 `JSNumberType` 來初始化 `JSValue`。
    public init(_ value: JSNumberType) {
        self.value = JSBackingValue.JSNumber(value)
    }

    /// 使用 `JSBoolType` 來初始化 `JSValue`。
    public init(_ value: JSBoolType) {
        self.value = JSBackingValue.JSBool(value)
    }

    /// 使用 `Error` 來初始化 `JSValue`。
    init(_ error: Error) {
        self.value = JSBackingValue.Invalid(error)
    }

    /// 使用 `JSBackingValue` 來初始化 `JSValue`。
    init(_ value: JSBackingValue) {
        self.value = value
    }
}

華麗的參數

相較于 Objective-C,Swift 有很多額外的選項用來指定可以傳入的參數的類型,下面是一些例子。

可選參數類型

在 Swift 中有一個新的概念稱之為 optional types

可選表示 “那兒有一個值,并且它等于 x ” 或者 “那兒沒有值”??蛇x有點像在 Objective-C 中使用 nil,但是它可以用在任何類型上,不僅僅是類??蛇x類型比 Objective-C 中的 nil 指針更加安全也更具表現力,它是 Swift 許多強大特性的重要組成部分。

表明一個參數是可選 (可以是 nil),可以在類型規(guī)范后添加一個問號:

func myFuncWithOptionalType(parameter: String?) {
    // function execution
}

myFuncWithOptionalType("someString")
myFuncWithOptionalType(nil)

使用可選時候不要忘記拆包!

func myFuncWithOptionalType(optionalParameter: String?) {
    if let unwrappedOptional = optionalParameter {
        println("The optional has a value! It's \(unwrappedOptional)")
    } else {
        println("The optional is nil!")
    }
}

myFuncWithOptionalType("someString")
// optional has a value! It's someString

myFuncWithOptionalType(nil)
// The optional is nil

如果學習過 Objective-C,那么習慣使用可選值肯定需要一些時間!

參數默認值

func hello(name: String = "you") {
    println("hello, \(name)")
}

hello(name: "Mr. Roboto")
// hello, Mr. Roboto

hello()
// hello, you

值得注意的是有默認值的參數自動包含一個外部參數名

由于參數的默認值可以在函數被調用時調過,所以最佳實踐是把含有默認值的參數放在函數參數列表的最后。Swift Programming Language Book 包含相關的內容介紹:

把含有默認值的參數放在參數列表最后,可以確保對它的調用中所有無默認值的參數順序一致,而且清晰表述了在不同情況下調用的函數是相同的。

我是默認參數的粉絲,主要是它使得代碼容易改變而且向后兼容。比如配置一個自定義的 UITableViewCell 的函數里,你可以在你的某個用例中用兩個參數開始,如果另一個用例出現,需要另一個參數 (比如你的 Cell 的 label 含有不同文字顏色),只需要添加一個包含新默認值的參數 — 函數的其他部分已經被正確調用,并且你代碼最新部分僅需要參數傳入一個非默認值。

可變參數

可變參數是傳入數組元素的一個更加可讀的版本。實際上,比如下面例子中的內部參數名類型,你可以看到它是 [String] 類型 (String 數組):

func helloWithNames(names: String...) {
    for name in names {
        println("Hello, \(name)")
    }
}

// 2 names
helloWithNames("Mr. Robot", "Mr. Potato")
// Hello, Mr. Robot
// Hello, Mr. Potato

// 4 names
helloWithNames("Batman", "Superman", "Wonder Woman", "Catwoman")
// Hello, Batman
// Hello, Superman
// Hello, Wonder Woman
// Hello, Catwoman

這里要特別記住的是可以傳入 0 個值,就像傳入一個空數組一樣,所以如果有必要的話,不要忘記檢查空數組:

func helloWithNames(names: String...) {
    if names.count > 0 {
        for name in names {
            println("Hello, \(name)")
        }
    } else {
        println("Nobody here!")
    }
}

helloWithNames()
// Nobody here!

可變參數另一個要注意的地方是 — 可變參數必須是在函數列表的最后一個!

輸入輸出參數 inout

利用 inout 參數,你有能力 (經過引用來) 操縱外部變量:

var name1 = "Mr. Potato"
var name2 = "Mr. Roboto"

func nameSwap(inout name1: String, inout name2: String) {
    let oldName1 = name1
    name1 = name2
    name2 = oldName1
}

nameSwap(&name1, &name2)

name1
// Mr. Roboto

name2
// Mr. Potato

這是 Objective-C 中非常常見的用來處理錯誤的模式。 NSJSONSerialization 是其中一個例子:

- (void)parseJSONData:(NSData *)jsonData
{
    NSError *error = nil;
    id jsonResult = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];

    if (!jsonResult) {
        NSLog(@"ERROR: %@", error.description);
    }
}

Swift 非常之新,所以這里沒有一個公認的處理錯誤的方式,但是在 inout 參數之外肯定有非常多的選擇!看看 David Owen's 最新的博客 Swfit 中的錯誤處理。關于這個話題的更多內容已經在 Functional Programming in Swift 中被涵蓋.

泛型參數類型

我不會在本文中大篇幅介紹泛型,但是這里有個簡單的例子來闡述如何在一個函數中接受兩個類型不定的參數,但確保這兩個參數類型是相同的:

func valueSwap<T>(inout value1: T, inout value2: T) {
    let oldValue1 = value1
    value1 = value2
    value2 = oldValue1
}

var name1 = "Mr. Potato"
var name2 = "Mr. Roboto"

valueSwap(&name1, &name2)

name1 // Mr. Roboto
name2 // Mr. Potato

var number1 = 2
var number2 = 5

valueSwap(&number1, &number2)

number1 // 5
number2 // 2

更多的泛型知識,我建議你閱讀下 Swift Programming Language book 中的泛型章節(jié)。

變量參數 var

默認情況下,參數傳入函數是一個常量,所以它們在函數范圍內不能被操作。如果你想修改這個行為,只需要在你的參數前使用 var 關鍵字:

var name = "Mr. Roboto"

func appendNumbersToName(var name: String, #maxNumber: Int) -> String {
    for i in 0..<maxNumber {
        name += String(i + 1)
    }
    return name
}

appendNumbersToName(name, maxNumber:5)
// Mr. Robot12345

name
// Mr. Roboto

值得注意的是這個和 inout 參數不同 — 變量參數不會修改外部傳入變量!

作為參數的函數

在 Swift 中,函數可以被用來當做變量傳遞。比如,一個函數可以含有一個函數類型的參數:

func luckyNumberForName(name: String, #lotteryHandler: (String, Int) -> String) -> String {
    let luckyNumber = Int(arc4random() % 100)
    return lotteryHandler(name, luckyNumber)
}

func defaultLotteryHandler(name: String, luckyNumber: Int) -> String {
    return "\(name), your lucky number is \(luckyNumber)"
}

luckyNumberForName("Mr. Roboto", lotteryHandler: defaultLotteryHandler)
// Mr. Roboto, your lucky number is 38

注意下只有函數的引用被傳入 — 在本例中是 defaultLotteryHandler。這個函數之后是否執(zhí)行是由接收的函數決定。

實例方法也可以用類似的方法傳入:

func luckyNumberForName(name: String, #lotteryHandler: (String, Int) -> String) -> String {
    let luckyNumber = Int(arc4random() % 100)
    return lotteryHandler(name, luckyNumber)
}

class FunLottery {

    func defaultLotteryHandler(name: String, luckyNumber: Int) -> String {
        return "\(name), your lucky number is \(luckyNumber)"
    }

}

let funLottery = FunLottery()
luckyNumberForName("Mr. Roboto", lotteryHandler: funLottery.defaultLotteryHandler)
// Mr. Roboto, your lucky number is 38

為了讓你的函數定義更具可讀性,可以考慮為你函數的類型創(chuàng)建別名 (類似于 Objective-C 中的 typedef):

typealias lotteryOutputHandler = (String, Int) -> String

func luckyNumberForName(name: String, #lotteryHandler: lotteryOutputHandler) -> String {
    let luckyNumber = Int(arc4random() % 100)
    return lotteryHandler(name, luckyNumber)
}

你也可以使用不包含參數名的函數 (類似于 Objective-C 中的 blocks):

func luckyNumberForName(name: String, #lotteryHandler: (String, Int) -> String) -> String {
    let luckyNumber = Int(arc4random() % 100)
    return lotteryHandler(name, luckyNumber)
}

luckyNumberForName("Mr. Roboto", lotteryHandler: {name, number in
    return "\(name)'s' lucky number is \(number)"
})
// Mr. Roboto's lucky number is 74

在 Objective-C 中,使用 blocks 作為參數是異步操作是操作結束時的回調和錯誤處理的常見方式,這一方式在 Swift 中得到了很好的延續(xù)。

權限控制

Swift 有三個級別的權限控制

  • Public 權限 可以為實體啟用定義它們的模塊中的源文件的訪問,另外其他模塊的源文件里只要導入了定義模塊后,也能進行訪問。通常情況下,Framework 是可以被任何人使用的,你可以將其設置為 public 級別
  • Internal 權限 可以為實體啟用定義它們的模塊中的源文件的訪問,但是在定義模塊之外的任何源文件中都不能訪問它。通常情況下,app 或 Framework 的內部結構使用 internal 級別。
  • Private 權限 只能在當前源文件中使用的實體。使用 private 級別,可以隱藏某些功能的特地的實現細節(jié)。

默認情況下,每個函數和變量是 internal 的 —— 如果你希望修改他們,你需要在每個方法和變量的前面使用 private 或者 public 關鍵字:

public func myPublicFunc() {

}

func myInternalFunc() {

}

private func myPrivateFunc() {

}

private func myOtherPrivateFunc() {

}

Ruby 帶來的習慣,我喜歡把所有的私有函數放在類的最下面,利用一個 //MARK 來區(qū)分:

class MyFunClass {

    func myInternalFunc() {

    }

    // MARK: Private Helper Methods

    private func myPrivateFunc() {

    }

    private func myOtherPrivateFunc() {

    }
}

希望 Swift 在將來的版本中包含一個選項可以用一個私有關鍵字來表明以下所有的方法都是私有的,類似于其他語言那樣做訪問控制。

華麗的返回類型

在 Swift 中,函數的返回類型和返回值相較于 Objective-C 而言更加復雜,尤其是引入可選和多個返回類型。

可選返回類型

如果你的函數有可能返回一個 nil 值,你需要指定返回類型為可選:

func myFuncWithOptonalReturnType() -> String? {
    let someNumber = arc4random() % 100
    if someNumber > 50 {
        return "someString"
    } else {
        return nil
    }
}

myFuncWithOptonalReturnType()

當然,當你使用可選返回值,不要忘記拆包:

let optionalString = myFuncWithOptonalReturnType()

if let someString = optionalString {
    println("The function returned a value: \(someString)")
} else {
    println("The function returned nil")
}

The best explanation I've seen of optionals is from a tweet by @Kronusdark: 對我而言最好的可選值的解釋來自于 @Kronusdark 的一條推特:

我終于弄明白 @SwiftLang 的可選值了,它們就像薛定諤的貓!在你使用之前,你必須先看看貓是死是活。

多返回值

Swift 其中一個令人興奮的功能是函數可以有多個返回值

func findRangeFromNumbers(numbers: Int...) -> (min: Int, max: Int) {

    var min = numbers[0]
    var max = numbers[0]

    for number in numbers {
        if number > max {
            max = number
        }

        if number < min {
            min = number
        }
    }

    return (min, max)
}

findRangeFromNumbers(1, 234, 555, 345, 423)
// (1, 555)

就像你看到的那樣,在一個元組中返回多個值,這一個非常簡單的將值進行組合的數據結構。有兩種方法可以使用多返回值的元組:

let range = findRangeFromNumbers(1, 234, 555, 345, 423)
println("From numbers: 1, 234, 555, 345, 423. The min is \(range.min). The max is \(range.max).")
// From numbers: 1, 234, 555, 345, 423. The min is 1. The max is 555.

let (min, max) = findRangeFromNumbers(236, 8, 38, 937, 328)
println("From numbers: 236, 8, 38, 937, 328. The min is \(min). The max is \(max)")
// From numbers: 236, 8, 38, 937, 328. The min is 8. The max is 937

多返回值與可選值

當返回值是可選的時候,多返回值就比較棘手。對于多個可選值的返回,有兩種辦法解決這種情況。

在上面的例子函數中,我的邏輯是有缺陷的 —— 它有可能沒有值傳入,所以我的代碼有可能在沒有值傳入的時候崩潰,所以我希望我整個返回值是可選的:

func findRangeFromNumbers(numbers: Int...) -> (min: Int, max: Int)? {

    if numbers.count > 0 {

        var min = numbers[0]
        var max = numbers[0]

        for number in numbers {
            if number > max {
                max = number
            }

            if number < min {
                min = number
            }
        }

        return (min, max)
    } else {
        return nil
    }
}

if let range = findRangeFromNumbers() {
    println("Max: \(range.max). Min: \(range.min)")
} else {
    println("No numbers!")
}
// No numbers!

另一種做法是對元組中的每個返回值設為可選來代替整體的元組可選:

func componentsFromUrlString(urlString: String) -> (host: String?, path: String?) {
    let url = NSURL(string: urlString)
    return (url.host, url.path)
}

如果你決定你元組值中一些值是可選,拆包時候會變得有些復雜,你需要考慮每中單獨的可選返回值的組合:

let urlComponents = componentsFromUrlString("http://name.com/12345;param?foo=1&baa=2#fragment")

switch (urlComponents.host, urlComponents.path) {
case let (.Some(host), .Some(path)):
    println("This url consists of host \(host) and path \(path)")
case let (.Some(host), .None):
    println("This url only has a host \(host)")
case let (.None, .Some(path)):
    println("This url only has path \(path). Make sure to add a host!")
case let (.None, .None):
    println("This is not a url!")
}
// This url consists of host name.com and path /12345

如你所見,它和 Objective-C 的處理方式完全不同!

返回一個函數

Swift 中函數可以返回一個函數:

func myFuncThatReturnsAFunc() -> (Int) -> String {
    return { number in
        return "The lucky number is \(number)"
    }
}

let returnedFunction = myFuncThatReturnsAFunc()

returnedFunction(5) // The lucky number is 5

為了更具有可讀性,你當然可以為你的返回函數定義一個別名:

typealias returnedFunctionType = (Int) -> String

func myFuncThatReturnsAFunc() -> returnedFunctionType {
    return { number in
        return "The lucky number is \(number)"
    }
}

let returnedFunction = myFuncThatReturnsAFunc()

returnedFunction(5) // The lucky number is 5

嵌套函數

如果在這篇文章中你沒對函數有足夠的體會,那么了解下 Swift 可以在函數中定義函數也是不錯的。

func myFunctionWithNumber(someNumber: Int) {

    func increment(var someNumber: Int) -> Int {
        return someNumber + 10
    }

    let incrementedNumber = increment(someNumber)
    println("The incremeted number is \(incrementedNumber)")
}

myFunctionWithNumber(5)
// The incremeted number is 15

@end

Swift 函數有更多的選項以及更為強大功能。從你開始利用 Swift 編程時,記?。耗芰υ綇娯熑卧酱蟆U堃欢ㄒ擅畹貎?yōu)化可讀性!

Swift 的最佳實踐還沒被確立,這門語言也在不斷地進化,所以請朋友和同事來審查你的代碼。我發(fā)現一些從來沒見過 Swift 的人反而在我的 Swift 代碼中提供了很大幫助。

Swift 編碼快樂!