雜談Apple Swift

RobinQu發表於2014-06-04

Apple的WWDC在昨天圓滿結束,期望iPhone6的同學可能很失望,但是對於程式設計師們,他們又多了好多新玩具。除了成堆的新API,Apple還發布了一款全新的程式語言——Swift

早就有謠言Apple會想辦法替換掉ObjectiveC,但大家都沒當回事。可這次,Apple直接放了大招。

並不是Apple喜歡折騰,是ObjectiveC的確不招人喜歡:

  1. 作為一個C語言的變種,它有較為陡峭的學習曲線
  2. 缺乏動態特性。雖然新版的ObjectiveC已經非常努力的更新很多看起來很NB的東西,但是這在其他語言確實天生支援的。
    1. Java直到8才支援lamda,但ObjectiveC的2013年就支援Block了。不過Block的限制太多,而且語法醜陋
    2. idinstancetype這些東西的產生都是很無奈的,沒法徹底解放複雜的型別申明
    3. 說好的generic和auto-boxing呢
  3. [[ObjectievC 的語法] 太羅嗦]
  4. …(歡迎補充)

Swift釋出後,我第一時間下載了免費的教程,看完了Language Guide後寫下我第一篇關於Swift的文章。

可以預料,網上已經有很多文章在說Swift又如何如何的好了。它到底好不好,我們這打個問號。先讓說說它十分有特色的地方,順便附上免費吐槽。

Swift的產生

Swift is a new programming language for iOS and OS X apps that builds on the best of C and Objective-C, without the constraints of C compatibility.

我們解讀下Swift程式設計指南中開篇的第一句話:

  1. 它是為了編寫iOS和OSX的應用而產生的
  2. 它吸取了C和ObjectiveC的優點
  3. 它去掉了為了實現對C相容性而做出的妥協

Swift的產生是面向未來的,並且是冷靜分析了當前Apple軟體生態圈之後歸納總結出新語言。程式設計指南的開篇也提到,Apple內部已經使用了Swift多年。

事實上,有LLVM在前,大家應該不難想像Apple可以搞出很多種高階語言出來,反正只要能通過LLVM編譯,就能接入Apple的生態體系了。看看隔壁的微軟吧,一個.net上面有多少種語言?

Swift也不是Apple的第一次折騰。從Carbon到Cocoa,從MRC到ARC,大家都是含著淚走過來的。其實,Swift也並不能宣判ObjectiveC的死刑,我相信,對於這種翻天覆地的改動,就算一切順利,也需要一年左右的時間讓開發者接受。Apple很可能會觀察社群動向,同時對兩種語言進行調整,最壞的情況下,有一部分市場的應用仍舊使用ObjectiveC,而另外一部分,尤其是遊戲,很可能徹底拋棄ObjectiveC,而使用更敏捷的Swift。

Swift的定位,意味著它有豐富的動態特性,同時擁有靜態語言的效能和安全性。你可以通過XCode6的Playground專案進行實驗,也可以直接通過Swift編譯器進行除錯。Swift Compiler位於:

/Applications/Xcode6-Beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift

變數和資料型別

let it be

Swift的所有變數都需要用let或者var宣告。這兩個關鍵字的在別的語言廣泛存在:ECMAScript(JavaScript、ActionScript)、Go。Swift也並不要求開發者一定需要寫明其資料型別,因為編譯器有一定能力推斷變數的資料型別。

let implicitInteger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70

注意,這並不是說Swift變成弱型別了。事實上,Swift的變數一旦確定了型別,便不能改變。而且,Swift也不存在隱式轉換。例如,如果需要字串拼接,需要你手動轉換:

let widthLabel = label + String(width)

Generics

let emptyDictionary = Dictionary<String, Float>()

ObjectiveC的資料結構最大的詬病就是不支援Generic了。Swift還提供了兩個萬精油的資料型別:

  1. AnyObject
  2. Any

相當於於void*id了,這些東西聯合起來可以說是讓大家對資料結構的應用更靈活一些了。

資料型別

除了常見的Int、UInt、Float、Double、Bool之外,它還有目前動態語言喜歡使用的Tuple。

var my2dPosition = (20, 20)
var my3dPosition = (20, 10, 50)

這也意味著,支援返回多個數值和Deconstructing了。

var (a,b) = (10,20)

然後就是Range,一種全閉合,一種半開半閉。

  • 1…3
  • 1..3

函式

好吧,Swift函式也很難看。它看起來像是Go、Ruby的結合體。但從特性上來講,Swift的函式是非常優秀的。

  • Functions are a first-class type
  • 可變的函式列表
  • Closure
    • 尾部Closure呼叫
    • 捕獲當前作用域
  • Local and external argument names

這部分將開來講太複雜了。可以說該有的動態語言特性都有了。

流程控制

更嚴格的if語句在判斷條件時,並不考慮0,而只考慮Boolean。

功能無比強大的switch再也不是雞肋了:

let somePoint = (1, 1)
switch somePoint {
case (0, 0):
    println("(0, 0) is at the origin")
case (_, 0):
    println("((somePoint.0), 0) is on the x-axis")
case (0, _):
    println("(0, (somePoint.1)) is on the y-axis")
case (-2...2, -2...2):
    println("((somePoint.0), (somePoint.1)) is inside the box")
default:
    println("((somePoint.0), (somePoint.1)) is outside of the box")
}
// prints "(1, 1) is inside the box

enum、class、struct

enum放在這裡,是因為Swift裡面的enum的確都是一個類了。

enum Rank: Int {
    case Ace = 1
    case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
    case Jack, Queen, King
    func simpleDescription() -> String {
        switch self {
        case .Ace:
            return "ace"
        case .Jack:
            return "jack"
        case .Queen:
            return "queen"
        case .King:
            return "king"
        default:
            return String(self.toRaw())
        }
    }
}

面對如此凶殘的enum我們怎麼把持的住?

Swift的類繼承可能時Swift裡最接近C、C++的地方了。無論是它的預設建構函式、解構函式,還是它後面提到的操作符過載都是C的那套思想,只是表現形式換了下。這意味著,這些東西很麻煩。

而同時又有動態語言常有的gettersetter那套。

struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
    get {
        let centerX = origin.x + (size.width / 2)
        let centerY = origin.y + (size.height / 2)
        return Point(x: centerX, y: centerY)
    }
    set(newCenter) {
        origin.x = newCenter.x - (size.width / 2)
        origin.y = newCenter.y - (size.height / 2)
    }
  }
}

每個Property還有兩個特殊的observer:

class StepCounter {
    var totalSteps: Int = 0 {
    willSet(newTotalSteps) {
        println("About to set totalSteps to (newTotalSteps)")
    }
    didSet {
        if totalSteps > oldValue  {
            println("Added (totalSteps - oldValue) steps")
        }
    }
    }
}

其他的如ProtocolExtension什麼的依舊是有的,並且,兩者都可以當做Type作為宣告變數。

Optional Type

還不知道這個東西該怎麼翻譯成中文,直接上程式碼吧:

if let johnsStreet = john.residence?.address?.street {
    println("John`s street name is (johnsStreet).")
} else {
    println("Unable to retrieve the address.")
}
// prints "Unable to retrieve the address."

一個值要麼存在,要麼為nil。和if一起使用被成為Optional Binding

這恐怕是Swift裡最晦澀的概念了。由於Swift裡沒有指標,而ObjectiveC裡一個預設行為就是向nil物件的呼叫(傳送訊息)是沒有任何效果,這個特點要完整遷移到Swift裡是很困難的。Optional Type的確提供了這個可能, 但這也恐怕是Swift學習成本最大的地方了。

與Cocoa和CocoaTouch的混用

Swift可以和Cocoa、CocoaTouch進行檔案級別的混用,也就是說你的專案裡可以同時存在這兩種檔案。在Swift裡使用Cocoa的API也是可以的:

let dataViewController = storyboard.instantiateViewControllerWithIdentifier("DataViewController") as DataViewController

Apple通過了複雜的橋接實現了這些,但可以遠見之後會有很第三方多框架相容性問題了。

Swift已經不支援C和C++程式碼的混用了,要記住這是Swift產生的初衷之一。

期望和展望

個人預測它的流行程度很快會超過Go、Ruby,並與Python並排,之後會穩定上升,最終是否會超過ObjectiveC,甚至接近Java,都需要看Apple的佈局了。

XCode6內目前是支援以兩種不同的語言來編寫應用的,但不知道未來是否會慢慢淘汰ObjectiveC。Apple完全有能力維護兩套語言,問題在於它是否需要。在很多領域,開發者早就渴望一種充滿動態語言特性的開發語言了,想想iOS上那些內嵌Lua的應用吧。

但是,我覺得Swift並不該像它的前輩那樣停留在編寫幾個iOS或者Mac應用上面,它應該走出來,成為真正意義上的通用語言。