在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

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

Swift 方法的多面性

雖然 Objective-C 的語法相對于其他編程語言來說寫法有點奇怪,但是當你真正使用的時候它的語法還是相當?shù)暮唵?。下面有一些例子?/p>

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

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

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

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

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

以上是長文慎讀。一句話:函數(shù)是獨立的,而方法是函數(shù)封裝在類,結(jié)構(gòu)或者枚舉中的函數(shù)。

剖析 Swift 函數(shù)

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

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

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

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

現(xiàn)在讓我們看一個稍稍復雜的例子:

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

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

調(diào)用所有函數(shù)

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

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

hello("Mr. Roboto")

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

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"),你可能很難知道每一個參數(shù)代表什么。

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

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

hello(fromName: "Mr. Roboto")

上面函數(shù)中,fromName 是一個外部參數(shù),在函數(shù)被調(diào)用的時候?qū)⒈话ㄔ谡{(diào)用中。在函數(shù)內(nèi)執(zhí)行時,使用 name 這個內(nèi)部參數(shù)來對輸入進行引用。

如果你希望外部參數(shù)和內(nèi)部參數(shù)相同,你不需要寫兩次參數(shù)名:

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

hello(name: "Robot")

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

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

hello(name: "Robot")

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

調(diào)用方法

當被封裝在類 (或者結(jié)構(gòu),枚舉) 中時,方法的第一個參數(shù)名被外部包含,同時所有的后面的參數(shù)在方法調(diào)用時候被外部包含:

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")

因此最佳實踐是在方法名里包含第一個參數(shù)名,就像 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")

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

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

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) 函數(shù)

需要注意一個非常酷的是 Swift 中實例方法是柯里化函數(shù)。

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

如果我有一個類:

class MyHelloWorldClass {

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

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

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

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

所以實際上我可以這樣調(diào)用我的函數(shù):

let myHelloWorldClassInstance = MyHelloWorldClass()

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

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

在類,結(jié)構(gòu)體或者枚舉初始化的時候?qū)⒄{(diào)用一個特殊的 init 方法。在 Swift 中你可以像其他方法那樣定義初始化參數(shù):

class Person {

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

}

Person(name: "Mr. Roboto")

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

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

class Person {

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

}

Person(fromName: "Mr. Roboto")

當然,就像其他方法那樣,如果你想讓方法跳過外部參數(shù)名的話,可以添加 _。我喜歡 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 

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

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

華麗的參數(shù)

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

可選參數(shù)類型

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

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

表明一個參數(shù)是可選 (可以是 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,那么習慣使用可選值肯定需要一些時間!

參數(shù)默認值

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

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

hello()
// hello, you

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

由于參數(shù)的默認值可以在函數(shù)被調(diào)用時調(diào)過,所以最佳實踐是把含有默認值的參數(shù)放在函數(shù)參數(shù)列表的最后。Swift Programming Language Book 包含相關(guān)的內(nèi)容介紹:

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

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

可變參數(shù)

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

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 個值,就像傳入一個空數(shù)組一樣,所以如果有必要的話,不要忘記檢查空數(shù)組:

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

helloWithNames()
// Nobody here!

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

輸入輸出參數(shù) inout

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

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 參數(shù)之外肯定有非常多的選擇!看看 David Owen's 最新的博客 Swfit 中的錯誤處理。關(guān)于這個話題的更多內(nèi)容已經(jīng)在 Functional Programming in Swift 中被涵蓋.

泛型參數(shù)類型

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

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é)。

變量參數(shù) var

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

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 參數(shù)不同 — 變量參數(shù)不會修改外部傳入變量!

作為參數(shù)的函數(shù)

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

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

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

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

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

為了讓你的函數(shù)定義更具可讀性,可以考慮為你函數(shù)的類型創(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)
}

你也可以使用不包含參數(shù)名的函數(shù) (類似于 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 作為參數(shù)是異步操作是操作結(jié)束時的回調(diào)和錯誤處理的常見方式,這一方式在 Swift 中得到了很好的延續(xù)。

權(quán)限控制

Swift 有三個級別的權(quán)限控制

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

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

public func myPublicFunc() {

}

func myInternalFunc() {

}

private func myPrivateFunc() {

}

private func myOtherPrivateFunc() {

}

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

class MyFunClass {

    func myInternalFunc() {

    }

    // MARK: Private Helper Methods

    private func myPrivateFunc() {

    }

    private func myOtherPrivateFunc() {

    }
}

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

華麗的返回類型

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

可選返回類型

如果你的函數(shù)有可能返回一個 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 其中一個令人興奮的功能是函數(shù)可以有多個返回值

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)

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

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

多返回值與可選值

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

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

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 的處理方式完全不同!

返回一個函數(shù)

Swift 中函數(shù)可以返回一個函數(shù):

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

let returnedFunction = myFuncThatReturnsAFunc()

returnedFunction(5) // The lucky number is 5

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

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

嵌套函數(shù)

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

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 函數(shù)有更多的選項以及更為強大功能。從你開始利用 Swift 編程時,記?。耗芰υ綇娯熑卧酱?。請一定要巧妙地優(yōu)化可讀性!

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

Swift 編碼快樂!