Swift 4遷移總結:喜憂參半,新的起點

沒故事的卓同學發表於2017-09-30

這次Swift 3 到 4 的遷移程式碼要改動的地方比較少,花了一個下午的時間就完成了遷移。Swift 把原來 4.0 的目標從 ABI 穩定改為了原始碼相容,此次程式碼的相容性做的確實很好,這個目標算是達到了。然而對於一個成熟的專案而言,單純語法上的相容並不是全部,這次的升級也帶來了一些新的變化。

3.2 和 4.0

在 3.0 的時候 swift 也提供了 2.3 和 3.0 兩個版本,這次 4.0 也是提供了 3.2 版本。從我專案裡的程式碼來看,從 3.0 到 3.2 要做的改動幾乎沒有,只是需要重新編譯一次。社群的反應來看相容 3.2 也沒反饋出什麼大的改動。所以對於推遲跟進 4.0 的團隊來言會是一個很順滑的過度,可以安心的切換到 Xcode 9。

為了讓大家在遷移過程中更加順利,swift 的 framework 支援 3.2 和 4.0 版本混編。如果你有好幾個元件,可以單獨為某個元件升級到 4.0 。這樣大的團隊可以不用一口氣所有的程式碼都遷移到 4.0。
然而 3.2 和 4.0 的相容並沒有看上去那麼美好。

首先是cocoapods的問題,反正在目前pods的framework只能指定一個版本的swift。issue在這裡:Pods automatically compiling with Swift 4.0 in Xcode 9 beta 1 。pod預設會使用一個swift版本編譯全部,需要對不同的庫單獨指定swift版本。

遇到這個問題後,我把所有元件都遷移到了 4.0 ,app 因為有很多業務程式碼,希望先遷到 3.2 ,這樣可以儘早支援 Xcode 9,同事可以儘早適配 iOS 11。然而。。。

Xcode 果然沒讓我失望啊,編譯的時候沒有錯誤提示只告訴你失敗了:

compiling as Swift 3.2, with 'xxx' build as Swift 4.0(this is supported but may expose additional compiler issues)

提示也很清奇,我翻譯一下:我們雖然支援混編,但是也可能混出毛病,所以你還是別混了。不過聽聞丁香園的專案目前是主 app 4.0,元件 3.2 混編是成功的。所以說呢,如果要混可以碰碰運氣。

順便說下Xcode 9新的編譯系統,我這裡根本編譯不過(可能因為我們混OC?),而且沒有任何錯誤提示。蘋果的軟體質量果然是獨領風騷,面向運氣程式設計。

社群跟進及時

所以實際上,你要不然就全不動,停在 3.2。一旦要遷移就需要全部遷移到 4.0 。

好在這次因為語法改動小,我用到的大部分 Swift 庫都支援了 4.0。棄更的只有 DOFavoriteButton ,一個點讚的控制元件。跟進比較慢的有 RxGesture、EZSwiftExtensions,不過已經也有了 4.0 的分支,也有人提了 4.0 的 PR,估計過幾天應該也能合進去。只能說維護的人不夠積極。
下面把我用到的 pod 貼出來:

3.2 4.0
RxSwift/RxCocoa 4.0.0-beta.0
SnapKit 4.0.0
CryptoSwift 0.7.0 0.7.1
Alamofire 4.5.1 4.5.1相容(5.0還未釋出)
ObjectMapper 2.2.9 3.0
SwiftyAttributes 3.2.0 4.0.0
Kingfisher 4.1.0 4.0
MonkeyKing 1.4.0

通常情況下3.0是可以直接在3.2下編譯的,所以“無”並不表示不能使用,指開發者沒有單獨宣告一個版本相容3.2。

4.0 開始與 OC 正式分道揚鑣

為了照顧原有的開發者,Swift 2.0 的時候要做到的目標是與 OC 儘量相容,除了幾個基礎的資料型別比如 Int、String 與OC不同外,其他的 API 都和 OC 保持一致,完全可以用 OC 的習慣寫 Swift 。到 3.0 的時候Swift 體系開始獨自進化,開始有自己的命名規範。

到 4.0 的時候,Xcode 用 Swift 重寫了編譯器,雖然 New build system 目前還在 preview,也確實有很多問題,然而針對 Swift 的編譯優化又取得了不小的提升。我已經能感覺到蘋果想要拋棄 OC 的意思,至少是非常明顯的嫌棄的意思。

從程式碼層面來看,原先一個類只要是 NSObject 的子類,預設這個類的所有的屬性方法都會自動新增給 OC 呼叫的 bridge,在 4.0 裡這個功能被關閉了。這也是遷移 4.0 的一個比較大的工作量(對於和 OC 混編的專案)。什麼意思呢,以前我們用 Swift 自定義了一個控制元件,原先在 OC 中引入 module 的標頭檔案後,可以呼叫到這個控制元件公開的所有屬性、方法。但是遷移到 4.0 後,所有屬性、方法預設都是不能訪問到,需要到控制元件裡給要暴露給 OC 使用的屬性方法前加上 @objc 。

這個改動影響非常深遠。這等於是讓開發者二選一了。如果我們用 Swift 寫一個元件,需要支援 OC 加上 @objc 標誌,編譯時就要生成給 OC 呼叫的宣告,這降低了一些些效能。但是不加的話 OC 又呼叫不到。更深的原因是,在寫元件的時候我們並不確切的知道業務方是用 OC 還是 Swift 呼叫的。除非業務程式碼全是 Swift。或者只能全盤做 OC 橋接,到處都是 @objc,如果上面呼叫的是 Swift,這些又白加了。

這裡還出現了另外一個細節。如果我們在 OC 裡給 UIView 宣告瞭一個屬性 size,在 Swift 裡也宣告瞭一個屬性 size。如果是在一個 framework 裡,會編譯失敗提示衝突了。然而如果這兩個寫在不同的 framework 裡,Xcode 不會提示。在 8 的時候,這個 size 的呼叫最後會走到 OC 的方法,但是在 9 的時候,在 Swift 程式碼裡引入這個 OC framework,程式碼就直接崩潰了,會不知道應該呼叫那個庫的 size。這顯然是編譯器的一個 bug,但這也側面反映了,OC 和 Swift 混編帶來的問題越來越多,兩個體系的區別會越來越大。

#總結
遷移到 4.0 的代價比之前要小的多, ABI 穩定很大可能在 5.0 到來。對於觀望 Swift 是否穩定的開發者而言是個好訊息,相信 Swift 的接受程度會更高。Swift 一年一個版本的升級和 OC 的分野會越來越大,給混編帶來了很多的不確定性,對於混編的專案有能力的還是把一些程式碼遷移到 Swift。

對於 Xcode 我有一個經驗再次和大家分享一下:Xcode 有兩個版本,一個不穩定的版本和一個更不穩定的版本。

歡迎在社交網路上關注我:

相關文章