2014年10月7日 星期二

如何讓 Swift 與 Objective-C 共生 - 無痛轉生

Hi There,  Swift 語言推出以來締造了許多的記錄,對於 iOS Developer 來說 Objective-C 是再熟悉不過了,現在有個新的小朋友 Swift 的出現,不知道需要需要再花時間來學習,這篇文章不是來教導怎麼學習 Swift 來開發 iOS 程式,而且先從 Swift 怎麼利用已定義好的 Objective-C 的 Class 來給想學或是正在學的 Developers 一個參考。
首先要介紹的是 Bridging Header 這個檔案。
首先 產生一個 Single View Application 的專案。如下圖
這個就是新的 Xcode 6 產生專案的畫面,比之前 Xcode 5 少了幾個 (少了幾個呢?留給讀者去比較了)。
接著我們要選擇 Swift 這個語言。如下
 命名為 SwiftObjc
然後我們要在專案的 Navigator 中新增一個 Objective-C 的 Class。如下。
記得要選的是 Cocoa Touch Class 而不是 Objective-C File 哦 。
命名為 Car。
記得 Language 是 Objective-C 哦。
接著不一樣的事情發生了。Xcode 會自動跳出一個提醒視窗。如下。

這個視窗的意思是說,Xcode 想幫開發者自動加入 bridging header 的檔案。
現在就先選擇 Yes 。來看看專案有什麼變化?如下
多了三個檔案哦。我們想要的 Car 這個 Class 的 .h, .m ,還有一個所謂的 Bridging Header 。在這的名稱為 SwiftObjC-Bridging-Header.h 。所以跳出視窗問的就是這個 Header,裡面長什麼樣子呢?基本上是空白。
因為我們要在 Swift Class 裡面用 Objective-C 的 Car class 。所以必需要加上如下的。程式碼。
就是 #import "Car.h" 。
也可以自已手動加,但需要改變專案的設定,如下圖
有一個 Objective-C Bridging Header 的設定要寫,記得除了寫 .h 檔名之外,還要加上 Product 名稱哦,通常就是 Project 名稱。
接下來我們就在 ViewController.swift 裡面使用 Car 這個 Class
如下
就可以直接在 viewDidLoad 裡面用 Car 這個 Class。而且用 Swift 語法
Car()
在 Swift 中 Car() 就是和 Objective-C 的
[[Car alloc] init ]
 而且聰明的讀者有沒有發現到,在 Swift 裡面每個 row 的程式碼最後都不需要分號 ; 結尾
接著我們來加一下 method 在 Car 裡面。如下新增

Method 的名稱叫 setPrice:andYear: 這樣的 selector 要怎麼在 Swift 呼叫呢?Swift 也是支援  selector 的觀念,如下使用。
setPrice:andYear: 在 Swift 的語法就是
func setPrice( a:Int, andYear: b:Int)
 這也是 Swift 跳脫了傳統的 C function 的束縛加了一個新的語法叫 external parameter name 。也就是 andYear: ,在 Swift 的 function 裡用第二個參數的 external parameter name 來表示。
接著執行是會 Crash。為什麼?因為 Car.m 並沒有實作 -setPrice:andYear:。所以記得要實作。如下。
順便加上兩個  ivar,各是 price 和 year
這樣一來在 ViewController.swift 就可以執行成功。
對於 Selector 的觀念,我們再來做一個小小的實驗。
先在 ViewController.swift 再加上一些程式碼

我們要利用 NSTimer 來呼叫兩個  function 。分別是
func action(a:NSTimer, second b:Int ) {
        println("action1")}
這個的 selector 寫成 Selector("action:second:")

以及
func action(action a:NSTimer, second b:NSTimer){
        println("action2") }
而這樣的寫法,無法對應到 Objective-C 觀念的 Selector
執行後會發現一直出現 action1 的 log是正常的。

接著我們來看一下存取 ivar。在這個時候如果直接從 Swift 存取 Car 的 ivar 會發生如下錯誤。


放在  .m 的 ivar 應該是無法被看到,如果試著把 ivar 拿到 .h 來如下

結果也是一樣。在 Swift 要正常存取 Objective-C 的 ivar 。最好的方式就是利用 property,將 Car.h 改寫如下


再利用 Swift 來執行如下
就可以成功執行。

最後要來解釋一下 Objective-C 的 class method 要怎麼在 Swift 被使用。
我們要在 Car.h 加上 class method 如下
當然 .m 要對應好

怎麼在 Swift 中使用 +sharedInstance 呢?如下

var myCar = Car.sharedInstance()

這樣就可以了。看起來不難,不過提醒讀者名稱這事對於 Objective-C 和 Swift 都很重要,如果有一個 class method 命名成如下
+(instancetype) car; 

在使用

var myCar = Car.car()
就會造成 compiler 的錯誤,目前可以的話就盡量避免,直接用 Class 名稱第一個字小寫的 class method。
談了這麼多,到底應用在真實的例子是如何呢?
我們就拿 AFNetworking 的 framework 來測試。

這是一個非常有名也很多人用在網路的資料的傳輸上的 open source 的 framework。
在這就不簡介。先把 AFNetworking 的檔案都拉到專案
然後在 Bridging Header File 也要加上 AFNetworking.h 如下
最後補上一段,下載 Image 的程式碼就大功告成了啦
最後回答大家常問麥老師的問題,Swift 是不是非學不可?Swift 是不是會取代 Objective-C ?
Swift 是不是非學不可?
在這一二年內答案不是,但是因為 Swift 的好學好寫,我想再過不久 Swift 的 framework 不會比 Objective-C 少,到時,如果用到只有 Swift 版的 framework 就是非學不可了。
Swift 是不是會取代 Objective-C ?
我個人是覺得再 5 年可能有機會,因為一個語言從出生到非常成熟可能需要 10 的年磨練,依照熱門程度不同,時間會有所調整,Swift 這麼火紅的情況下,可能會使它比較快穩定。我們可以看一個參考,如果 Apple 某個常用應用程式是用 Swift 重新改寫,完全取代 Objective-C 的日子就很近了。

以上一些,很久沒有寫文章的碎碎唸。希望大家有所收獲。請不吝指教。Michael 筆。

沒有留言:

張貼留言