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

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

Swift 方法的多面性

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

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

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

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

在繼續(xù)之前,我需要澄清 Swift 中方法函數(shù)之間的不同,因?yàn)樵诒疚闹形覀儗⑹褂眠@兩個(gè)術(shù)語。按照 Apple 的 Swift Programming Language Book 里面的方法定義:

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

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

剖析 Swift 函數(shù)

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

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

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

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

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

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

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

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

Swift 和 Objective-C 之間其中一個(gè)巨大的差別就是當(dāng) Swift 函數(shù)被調(diào)用的時(shí)候參數(shù)工作方式。如果你像我一樣喜歡 Objective-C 超長的命名方式,那么請記住,在默認(rèn)情況下 Swift 函數(shù)被調(diào)用時(shí)參數(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"),你可能很難知道每一個(gè)參數(shù)代表什么。

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

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

hello(fromName: "Mr. Roboto")

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

如果你希望外部參數(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")

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

調(diào)用方法

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

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í)踐是在方法名里包含第一個(gè)參數(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,這使得第一個(gè)參數(shù) name 變得很清晰。

如果出于一些原因你希望在函數(shù)中跳過外部參數(shù)名 (我建議如果要這么做的話,你需要一個(gè)非常好的理由),為外部函數(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")

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

需要注意一個(gè)非??岬氖?Swift 中實(shí)例方法是柯里化函數(shù)。

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

如果我有一個(gè)類:

class MyHelloWorldClass {

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

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

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

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

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

let myHelloWorldClassInstance = MyHelloWorldClass()

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

初始化:一個(gè)特殊注意的地方

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

class Person {

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

}

Person(name: "Mr. Roboto")

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

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

class Person {

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

}

Person(fromName: "Mr. Roboto")

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

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 中對這項(xiàng)技術(shù)的使用:

public struct JSValue : Equatable {

    // ... 截?cái)嗟牟糠执a

    /// 使用 `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 有很多額外的選項(xiàng)用來指定可以傳入的參數(shù)的類型,下面是一些例子。

可選參數(shù)類型

在 Swift 中有一個(gè)新的概念稱之為 optional types

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

表明一個(gè)參數(shù)是可選 (可以是 nil),可以在類型規(guī)范后添加一個(gè)問號(hào):

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

myFuncWithOptionalType("someString")
myFuncWithOptionalType(nil)

使用可選時(shí)候不要忘記拆包!

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

如果學(xué)習(xí)過 Objective-C,那么習(xí)慣使用可選值肯定需要一些時(shí)間!

參數(shù)默認(rèn)值

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

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

hello()
// hello, you

值得注意的是有默認(rèn)值的參數(shù)自動(dòng)包含一個(gè)外部參數(shù)名

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

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

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

可變參數(shù)

可變參數(shù)是傳入數(shù)組元素的一個(gè)更加可讀的版本。實(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 個(gè)值,就像傳入一個(gè)空數(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ù)另一個(gè)要注意的地方是 — 可變參數(shù)必須是在函數(shù)列表的最后一個(gè)!

輸入輸出參數(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 中非常常見的用來處理錯(cuò)誤的模式。 NSJSONSerialization 是其中一個(gè)例子:

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

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

Swift 非常之新,所以這里沒有一個(gè)公認(rèn)的處理錯(cuò)誤的方式,但是在 inout 參數(shù)之外肯定有非常多的選擇!看看 David Owen's 最新的博客 Swfit 中的錯(cuò)誤處理。關(guān)于這個(gè)話題的更多內(nèi)容已經(jīng)在 Functional Programming in Swift 中被涵蓋.

泛型參數(shù)類型

我不會(huì)在本文中大篇幅介紹泛型,但是這里有個(gè)簡單的例子來闡述如何在一個(gè)函數(shù)中接受兩個(gè)類型不定的參數(shù),但確保這兩個(gè)參數(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

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

變量參數(shù) var

默認(rèn)情況下,參數(shù)傳入函數(shù)是一個(gè)常量,所以它們在函數(shù)范圍內(nèi)不能被操作。如果你想修改這個(gè)行為,只需要在你的參數(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

值得注意的是這個(gè)和 inout 參數(shù)不同 — 變量參數(shù)不會(huì)修改外部傳入變量!

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

在 Swift 中,函數(shù)可以被用來當(dāng)做變量傳遞。比如,一個(gè)函數(shù)可以含有一個(gè)函數(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。這個(gè)函數(shù)之后是否執(zhí)行是由接收的函數(shù)決定。

實(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é)束時(shí)的回調(diào)和錯(cuò)誤處理的常見方式,這一方式在 Swift 中得到了很好的延續(xù)。

權(quán)限控制

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

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

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

public func myPublicFunc() {

}

func myInternalFunc() {

}

private func myPrivateFunc() {

}

private func myOtherPrivateFunc() {

}

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

class MyFunClass {

    func myInternalFunc() {

    }

    // MARK: Private Helper Methods

    private func myPrivateFunc() {

    }

    private func myOtherPrivateFunc() {

    }
}

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

華麗的返回類型

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

可選返回類型

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

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

myFuncWithOptonalReturnType()

當(dāng)然,當(dāng)你使用可選返回值,不要忘記拆包:

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

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)

就像你看到的那樣,在一個(gè)元組中返回多個(gè)值,這一個(gè)非常簡單的將值進(jìn)行組合的數(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

多返回值與可選值

當(dāng)返回值是可選的時(shí)候,多返回值就比較棘手。對于多個(gè)可選值的返回,有兩種辦法解決這種情況。

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

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!

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

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

如果你決定你元組值中一些值是可選,拆包時(shí)候會(huì)變得有些復(fù)雜,你需要考慮每中單獨(dú)的可選返回值的組合:

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

返回一個(gè)函數(shù)

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

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

let returnedFunction = myFuncThatReturnsAFunc()

returnedFunction(5) // The lucky number is 5

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

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ù)有足夠的體會(huì),那么了解下 Swift 可以在函數(shù)中定義函數(shù)也是不錯(cuò)的。

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ù)有更多的選項(xiàng)以及更為強(qiáng)大功能。從你開始利用 Swift 編程時(shí),記?。耗芰υ綇?qiáng)責(zé)任越大。請一定要巧妙地優(yōu)化可讀性!

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

Swift 編碼快樂!