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

鍍金池/ 教程/ iOS/ Swift 的強(qiáng)大之處
與四軸無人機(jī)的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動開發(fā)
Collection View 動畫
截圖測試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進(jìn)的自動布局工具箱
動畫
為 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
動畫解釋
響應(yīng)式 Android 應(yīng)用
初識 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)雅的移動游戲
繪制像素到屏幕上
相機(jī)與照片
音頻 API 一覽
交互式動畫
常見的后臺實(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 測試
值對象
活動追蹤
依賴注入
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 中自定義屬性的動畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲
代碼審查的藝術(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 的人臉識別
玩轉(zhuǎn)字符串
相機(jī)工作原理
Build 過程

Swift 的強(qiáng)大之處

在寫任何東西之前我需要承認(rèn)我是帶有偏見的:我愛 Swift。我認(rèn)為這是從我開始接觸 Cocoa 生態(tài)系統(tǒng)以來這個平臺上發(fā)生的最好的事情。我想通過分享我在 Swift,Objective-C 和 Haskell 上的經(jīng)驗(yàn)讓大家知道我為何這樣認(rèn)為。寫這篇文章并不是為了介紹一些最好的實(shí)踐 (寫這些的時(shí)候 Swift 還太年輕,還沒最好實(shí)踐被總結(jié)出來),而是舉幾個關(guān)于 Swift 強(qiáng)大之處的例子。

給大家一些我的個人背景:在成為全職 iOS/Mac 工程師之前我花了幾年的時(shí)間做 Haskell (包括一些其他函數(shù)式編程語言) 開發(fā)。我仍然認(rèn)為 Haskell 是我所有使用過的語言中最棒的之一。然而我轉(zhuǎn)戰(zhàn)到了 Objective-C,是因?yàn)槲蚁嘈?iOS 是最令人激動的平臺。剛開始接觸 Objective-C 的時(shí)候我有些許沮喪,但我慢慢地學(xué)會了欣賞它。

當(dāng)蘋果在 WWDC 發(fā)布 Swift 的時(shí)候我非常的激動。我已經(jīng)很久沒有對新技術(shù)的發(fā)布感的如此興奮了。在看過文檔之后我意識到 Swift 使我們能夠?qū)F(xiàn)有的函數(shù)式編程知識和 Cocoa API 無縫地整合到一起。我覺得這兩者的組合非常獨(dú)特:沒有任何其他的語言將它們?nèi)诤系厝绱送昝?。就?Haskell 來說,想要用它來使用 Objective-C API 相當(dāng)?shù)睦щy。同樣,想用 Objective-C 去做函數(shù)式編程也是十分困難的。

在 Utrecht 大學(xué)期間我學(xué)會了函數(shù)式編程。因?yàn)槭窃诤軐W(xué)術(shù)的環(huán)境下學(xué)習(xí)所以并沒有覺得很多復(fù)雜的術(shù)語 (moands,applicative functors 以及很多其他的東西) 有多么難懂。我覺得對很多想學(xué)習(xí)函數(shù)式編程的人來說這些名稱是一個很大的阻礙。

不僅僅名稱很不同,風(fēng)格也不一樣。作為 Objective-C 程序員,我們很習(xí)慣于面向?qū)ο缶幊?。而且因?yàn)榇蠖鄶?shù)語言不是面對對象編程就是與之類似,我們可以看懂很多不同語言的代碼。閱讀函數(shù)式編程語言的時(shí)候則大不相同 -- 如果你沒有習(xí)慣的話看起來簡直莫名其妙。

那么,為什么你要使用函數(shù)式編程呢?它很奇怪,很多人都不習(xí)慣而且學(xué)習(xí)它要花費(fèi)大量的時(shí)間。并且對于大多數(shù)問題面向?qū)ο缶幊潭寄芙鉀Q,所以沒有必要去學(xué)習(xí)任何新的東西對吧?

對于我來說,函數(shù)式編程只是工具箱中的一件工具。它是一個改變了我對編程的理解的強(qiáng)大工具。在解決問題的時(shí)候它非常強(qiáng)大。對于大多數(shù)問題面向?qū)ο缶幊潭己馨簦菍τ谄渌恍﹩栴}應(yīng)用函數(shù)式編程會給你帶來巨大的時(shí)間/精力的節(jié)省。

開始學(xué)習(xí)函數(shù)式編程或許有些痛苦。第一,你必須放手一些老的模式。而因?yàn)槲覀兒芏嗳顺D暧妹鎸ο蟮姆绞饺ニ伎?,做到這一點(diǎn)是很困難的。在函數(shù)式編程當(dāng)中你想的是不變的數(shù)據(jù)結(jié)構(gòu)以及那些轉(zhuǎn)換它們的函數(shù)。在面對對象編程當(dāng)中你考慮的是互相發(fā)送信息的對象。如果你沒有馬上理解函數(shù)式編程,這是一個好的信號。你的大腦很可能已經(jīng)完全適應(yīng)了用面對對象的方法來解決問題。

例子

我最喜歡的 Swift 功能之一是對 optionals 的使用。Optionals 讓我們能夠應(yīng)對有可能存在也有可能不存在的值。在 Objective-C 里我們必須在文檔中清晰地說明 nil 是否是允許的。Optionals 讓我們將這份責(zé)任交給了類型系統(tǒng)。如果你有一個可選值,你就知道它可以是 nil。如果它不是可選值,你知道它不可能是 nil。

舉個例子,看看下面一小段 Objective-C 代碼

- (NSAttributedString *)attributedString:(NSString *)input 
{
    return [[NSAttributedString alloc] initWithString:input];
}

看上去沒有什么問題,但是如果 input 是 nil, 它就會崩潰。這種問題你只能在運(yùn)行的時(shí)候才能發(fā)現(xiàn)。取決于你如何使用它,你可能很快能發(fā)現(xiàn)問題,但是你也有可能在發(fā)布應(yīng)用之后才發(fā)現(xiàn),導(dǎo)致用戶正在使用的應(yīng)用崩潰。

用相同的 Swift 的 API 來做對比。

extension NSAttributedString {
    init(string str: String)
}

看起來像對Objective-C的直接翻譯,但是 Swift 不允許 nil 被傳入。如果要達(dá)到這個目的,API 需要變成這個樣子:

extension NSAttributedString {
    init(string str: String?)
}

注意新加上的問號。這意味著你可以使用一個值或者是 nil。類非常的精確:只需要看一眼我們就知道什么值是允許的。使用 optionals 一段時(shí)間之后你會發(fā)現(xiàn)你只需要閱讀類型而不用再去看文檔了。如果犯了一個錯誤,你會得到一個編譯時(shí)警告而不是一個運(yùn)行時(shí)錯誤。

建議

如果可能的話避免使用 optionals。Optionals 對于使用你 API 的人們來說是一個多余的負(fù)擔(dān)。話雖如此,還是有很多地方可以很好使用它們。如果你有一個函數(shù)會因?yàn)橐粋€明顯的原因失敗你可以返回一個 optional。舉例來說,比如將一個 #00ff00 字符串轉(zhuǎn)換成顏色。如果你的參數(shù)不符合正確的格式,你應(yīng)該返回一個 nil 。

func parseColorFromHexString(input: String) -> UIColor? {
    // ...
}

如果你需要闡明錯誤信息,你可以使用 Either 或者 Result 類型 (不在標(biāo)準(zhǔn)庫里面)。當(dāng)失敗的原因很重要的時(shí)候,這種做法會非常有用。“Error Handling in Swift” 一文中有個很好的例子。

Enums

Enums 是一個隨 Swift 推出的新東西,它和我們在 Objective-C 中見過的東西都大不相同。在 Objective-C 里面我們有一個東西叫做 enums, 但是它們差不多就是升級版的整數(shù)。

我們來看看布爾類型。一個布爾值是兩種可能性 -- true 或者 false -- 中的一個。很重要的一點(diǎn)是沒有辦法再添加另外一個值 -- 布爾類型是封閉的。布爾類型的封閉性的好處是每當(dāng)使用布爾值的時(shí)候我們只需要考慮 true 或者 false 這兩種情況。

在這一點(diǎn)上面 optionals 是一樣的。總共只有兩種情況:nil 或者有值。在 Swift 里面布爾和 optional 都可以被定義為 enums。但有一個不同點(diǎn):在 optional enum 中有一種可能性有一個相關(guān)值。我們來看看它們不同的定義:

enum Boolean {
    case False
    case True
}

enum Optional<A> {
    case Nil
    case Some(A)
}

它們非常的相似。如果你把它們的名稱改成一樣的話,那么唯一的區(qū)別就是括號里的相關(guān)值。如果你給 optional 中的 Nil 情況也加上一個值,你就會得到一個 Either 類型:

enum Either<A,B> {
    case Left<A>
    case Right<B>
}

在函數(shù)式編程當(dāng)中,在你想表示兩件事情之間的選擇時(shí)候你會經(jīng)常用到 Either 類型。舉個例子:如果你有一個函數(shù)返回一個整數(shù)或者一個錯誤,你就可以用 Either<Int, NSError>。如果你想在一個字典中儲存布爾值或者字符串,你就可以使用 Either<Bool,String> 作為鍵。

理論旁白:有些時(shí)候 enums 被稱為 sum 類型,因?yàn)樗鼈兪菐讉€不同類型的總和。在 Either 類型的例子中,它們表達(dá)的是 A 類型和 B 類型的和。Structs 和 tuples 被稱為 product 類型,因?yàn)樗鼈兇韼讉€不同類型的乘積。參見“algebraic data types.”

理解什么時(shí)候使用 enums 什么時(shí)候使用其他的數(shù)據(jù)類型 (比如 class 或者 structs)會有一些難度。當(dāng)你有一個固定數(shù)量的值的集合的時(shí)候,enum 是最有用的。比如說,如果我們設(shè)計(jì)一個 Github API 的 wrapper,我們可以用 enum 來表示端點(diǎn)。比如有一個不需要任何參數(shù)的 /zen 的 API 端點(diǎn)。再比如為了獲取用戶的資料我們需要提供用戶名。最后我們顯示用戶的倉庫時(shí),我們需要提供用戶名以及一個值去說明是否從小到大地排列結(jié)果。

enum Github {
    case Zen
    case UserProfile(String)
    case Repositories(username: String, sortAscending: Bool)
}

定義 API 端點(diǎn)是很好的使用 enum 的場景。API 的端點(diǎn)是有限的,所以我們可以為每一個端點(diǎn)定義一個情況。如果我們在對這些端點(diǎn)使用 switch 的時(shí)候沒有包含所有情況的話,我們會被給予警告。所以說當(dāng)我們需要添加一個情況的時(shí)候我們需要更新每一個用到這個 enum 的函數(shù)。

除非能夠拿到源代碼,其他使用我們 enum 的人不能添加新的情況,這是一個非常有用的限制。想想要是你能夠加一種新情況到 Bool 或者 Optional 里會怎么樣吧 -- 所有用到 它的函數(shù)都需要重寫。

比如說我們正在開發(fā)一個貨幣轉(zhuǎn)換器。我們可以將貨幣給定義成 enum:

enum Currency {
    case Eur
    case Usd
}

我們現(xiàn)在可以做一個獲取任何貨幣符號的函數(shù):

func symbol(input: Currency) -> String {
    switch input {
        case .Eur: return "€"
        case .Usd: return "$"
    }
}

最后,我們可以用我們的 symbol 函數(shù),來依據(jù)系統(tǒng)本地設(shè)置得到一個很好地格式化過的字符串:

func format(amount: Double, currency: Currency) -> String {
    let formatter = NSNumberFormatter()
    formatter.numberStyle = .CurrencyStyle
    formatter.currencySymbol = symbol(currency)
    return formatter.stringFromNumber(amount)
}

這樣一來有一個很大的限制。我們可能會想讓我們 API 的使用者在將來可以修改一些情況。在 Objective-C 當(dāng)中向一個接口里添加更多類型的常見解決方法是子類化。在 Objective-C 里面理論上你可以子類化任何一個類,然后通過這種辦法來擴(kuò)展它。在 Swift 里面你仍然可以使用子類化,但是只能對 class 使用,對于 enum 則不行。然而,我們可以用另一種技術(shù)來達(dá)到目的 (這種辦法在 Objetive-C 和 Swift 的 protocol 中都可行)。

假設(shè)我們定義一個貨幣符號的協(xié)議:

protocol CurrencySymbol {
    func symbol() -> String
}

現(xiàn)在我們讓 Currency 類型遵守這個協(xié)議。注意我們可以將 input 參數(shù)去掉,因?yàn)檫@里它被作為 self 隱式地進(jìn)行傳遞:

extension Currency : CurrencySymbol {
   func symbol() -> String {
        switch self {
            case .Eur: return "€"
            case .Usd: return "$"
        }
    }
}

現(xiàn)在我們可以重寫 format 方法來格式化任何遵守我們協(xié)議的類型:

func format(amount: Double, currency: CurrencySymbol) -> String {
    let formatter = NSNumberFormatter()
    formatter.numberStyle = .CurrencyStyle
    formatter.currencySymbol = currency.symbol()
    return formatter.stringFromNumber(amount)
}

這樣一來我們將我們代碼的可延展性大大提升類 -- 任何遵守 CurrencySymbol 協(xié)議的類型都可以被格式化。比如說,我們建立一個新的類型來儲存比特幣,我們可以立刻讓它擁有格式化功能:

struct Bitcoin : CurrencySymbol {
    func symbol() -> String {
        return "B?"
    }
}

這是一種寫出具有延展性函數(shù)的很好的方法。通過使用一個需要遵守協(xié)議,而不是一個實(shí)實(shí)在在的類型,你的 API 的用戶能夠加入更多的類型。你仍然可以利用 enum 的靈活性,但是通過讓它們遵守協(xié)議,你可以更好地表達(dá)自己的意思。根據(jù)你的具體情況,你現(xiàn)在可以輕松地選擇是否開放你的 API。

類型安全

我認(rèn)為類型的安全性是 Swift 一個很大的優(yōu)勢。就像我們在討論 optionals 時(shí)看見的一樣,我們可以用一些聰明的手段將某些檢測從運(yùn)行時(shí)轉(zhuǎn)移到編譯時(shí)。Swift 中數(shù)組的工作方式就是一個例子:一個數(shù)組是泛型的,它只能容納一個類型的對象。將一個整數(shù)附加在一個字符組數(shù)組后面是做不到的。這樣以來就消滅了一個類的潛在 bug。(值得注意的是如果你需要同時(shí)將字符串或者整數(shù)放到一個數(shù)組里的話,你可以使用上面談到過的 Either 類型。)

再比如說,我們要將我們到貨幣轉(zhuǎn)換器延展為一個通用的單位換算器。如果我們使用 Double 去表示數(shù)量,會有一點(diǎn)點(diǎn)誤導(dǎo)性。比如說,100.0 可以表示 100 美元,100 千克或者任何能用 100 表示的東西。我們可以借助類型系統(tǒng)來制作不同的類型來表示不同的物理上的數(shù)量。比如說我們可以定義一個類型來表示錢:

struct Money {
    let amount : Double
    let currency: Currency
}

我們可以定義另外一個結(jié)構(gòu)來表示質(zhì)量:

struct Mass {
    let kilograms: Double
}

現(xiàn)在我們就消除了不小心將 MoneyMass 相加的可能性?;谀銘?yīng)用的特質(zhì)有時(shí)候?qū)⒁恍┖唵蔚念愋桶b成這樣是很有效的。不僅如此,閱讀代碼也會變得更加簡單。假設(shè)我們遇到一個 pounds 函數(shù):

func pounds(input: Double) -> Double

光看類型定義很難看出來這個函數(shù)的功能。它將歐元裝換成英鎊?還是將千克轉(zhuǎn)換成磅? (英文中英鎊和磅均為 pound) 我們可以用不同的名字,或者可以建立文檔 (都是很好的辦法),但是我們有第三種選擇。我們可以將這個類型變得更明確:

func pounds(input: Mass) -> Double

我們不僅讓這個函數(shù)的用戶能夠立刻理解這個函數(shù)的功能,我們也防止了不小心傳入其他單位的參數(shù)。如果你試圖將 Money 作為參數(shù)來使用這個函數(shù),編譯器是不會接受的。另外一個可能的提升是使用一個更精確的返回值?,F(xiàn)在它只是一個 Double

不可變性

Swift 另外一個很棒的功能是內(nèi)置的不可變性。在 Cocoa 當(dāng)中很多的 API 都已經(jīng)體現(xiàn)出了不可變性的價(jià)值。想了解這一點(diǎn)為什么如此重要,“Error Handling in Swift” 是一個很好的參考。比如,作為一個 Cocoa 開發(fā)者,我們使用很多成對的類 (NSString vs. NSMutableString,NSArray vs. NSMutableArray)。當(dāng)你得到一個字符串值,你可以假設(shè)它不會被改變。但是如果你要完全確信,你依然要復(fù)制它。然后你才知道你有一份不可變的版本。

在 Swifit 里面,不可變性被直接加入這門語言。比如說如果你想建立一個可變的字符串,你可以如下的代碼:

var myString = "Hello"

然而,如果你想要一個不可變的字符串,你可以做如下的事情:

let myString = "Hello"

不可變的數(shù)據(jù)在創(chuàng)建可能會被未知用戶使用的 API 時(shí)會給你很大的幫助。比如說,你有一個需要字符串作為參數(shù)的函數(shù),在你迭代它的時(shí)候,確定它不會被改變是很重要的。在 Swift 當(dāng)中這是默認(rèn)的行為。正是因?yàn)檫@個原因,在寫多線程代碼的時(shí)候使用不可變資料會使難度大大降低。

還有另外一個巨大的優(yōu)勢。如果你的函數(shù)只使用不可變的數(shù)據(jù),你的類型簽名就會成為很好的文檔。在 Objective-C 當(dāng)中則不然。比如說,假設(shè)你準(zhǔn)備在 OS X 上使用 CIFilter。在實(shí)例化之后你需要使用 setDefaults 方法。這一點(diǎn)在文檔中有提到。有很多這樣類都是這個樣子。在實(shí)例化之后,在你使用它之前你必須要使用另外一個方法。問題在于,如果不閱讀文檔的話,經(jīng)常會不清楚哪些函數(shù)需要被使用,最后你有可能遇到很奇怪的狀況。

當(dāng)使用不可變資料的時(shí)候,類型簽名讓事情變得很清晰。比如說,map 的類簽名。我們知道有一個可選的 T 值,而且有一個將 T 轉(zhuǎn)換成 U 的函數(shù)。結(jié)果是一個可選的 U 值。原始值是不可能改變的:

func map<T, U>(x: T?, f: T -> U) -> U?

對于數(shù)組的 map 來說是一樣的。它被定義成一個數(shù)組的延伸,所以參數(shù)本身是 self。我們可以看到它用一個函數(shù)將 T 轉(zhuǎn)化成 U,并且生成一個 U 的數(shù)組。因?yàn)樗且粋€不可變的函數(shù),我們知道原數(shù)組是不會變化的,而且我們知道結(jié)果也是不會改變的。將這些限制內(nèi)置在l類型系統(tǒng)中,并有編譯器來監(jiān)督執(zhí)行,讓我們不再需要去查看文檔并記住什么會變化。

extension Array {
    func map<U>(transform: T -> U) -> [U]
}

總結(jié)

Swift 帶來了很多有趣的可能性。我尤其喜歡的一點(diǎn)是過去我們需要手動檢測或者閱讀文檔的事情現(xiàn)在編譯器可以幫我們來完成。我們可以選擇在合適的時(shí)機(jī)去使用這些可能性。我們依然會用我們現(xiàn)有的,成熟的辦法去寫代碼,但是我們可以在合適的時(shí)候在我們代碼的某些地方應(yīng)用這些新的可能性。

我預(yù)測:Swift 會很大程度上改變我們寫代碼的方式,而且是向好的方向改變。脫離 Objective-C 會需要幾年的時(shí)間,但是我相信我們中的大多數(shù)人會做出這個改變并且不會后悔。有些人會很快的適應(yīng),對另外一些人可能會花上很長的時(shí)間。但是我相信總有一天絕大多數(shù)人會看到 Swift 帶給我們的種種好處。