零. 前言
Swift 版本升級嘛,大家應該都很熟練了,選單 -> Edit -> Convert -> To Current Swift Syntax...,然後巴拉巴拉一頓操作。emmmn,抱歉,編譯過了也不一定能正常使用。
這次 Swift 3 到 Swift 4 的更新和之前的大版本更新相比,已經平滑了很多,相較之前的動輒幾百上千個 error,現在用 Xcode 進行 Convert 之後基本上只需要進行少量人工修正即可,不過仍然有一些點需要注意,本文將會對一些常見的坑或者注意點以及解決方法進行討論。
本文以 EFCountingLabel 的 1.0.3 版本和 Xcode 9.0 為例,主要關於原有的 Swift 3 的 CocoaPods 庫到 Swift 4 的升級,仍處於 Swift 2 階段的同學可暫時忽略本文。
一. 升級流程
1. 檢視當前版本
首先用 Xcode 開啟工程,看一下當前工程設定的 Swift 版本,如果過低的話可能無法直接 Convert,選中需要轉換的 target 搜尋 swift_ver
即可,如圖所示:
這裡 EFCountingLabel 的 Swift 版本為 3.2,如果是 2.x 的話需要自己想辦法先轉換成 Swift 3.x...
2. Xcode 程式碼轉換
接下來,就是利用 Xcode 實現程式碼轉換了,選單 -> Edit -> Convert -> To Current Swift Syntax...,然後選中需要轉換的 target,點選 Next
按鈕即可:
3. 選擇轉換模式
然後會出現一個轉換模式選項,有 Minimize Inference(recommended)
和 Match Swift 3 Behavior
兩個選擇,蘋果推薦的是第一個選項:
蘋果官方文件對這兩個選項的描述如下,大意是:如果選第一個選項,會僅在必要的時候為方法或屬性新增 @objc
標誌,不過大部分工作需要使用者(也就是你)手動完成,好處是能減少最終生成的二進位制檔案的大小;如果選擇第二個選項,則會按 Swift 3 的方式給所有的地方直接新增 @objc
標誌(關於 @objc
標誌的介紹大家可以參考 Swift 翻譯組的這篇文章),缺點就是不會對生成的二進位制檔案大小進行優化(也就是跟 Swift 3 一樣):
這裡我們分幾種情況:
- 如果你的 Swift 庫不打算支援 OC 呼叫的話,選
Minimize Inference(recommended)
,檢查並且儲存自動轉換結果即可,然後可以直接跳到下一小節,請忽略下面這一大段; - 如果你的 Swift 庫打算支援 OC 呼叫,但是開發時間緊迫暫時沒時間仔細設定
@objc
標誌或者對這一點二進位制檔案體積的縮減並不是十分在意的話,選Match Swift 3 Behavior
,檢查並且儲存自動轉換結果即可,然後可以直接跳到下一小節,請忽略下面這一大段; - 如果你的 Swift 庫打算支援 OC 呼叫,並且打算用推薦的方式進行優化的話,選
Minimize Inference(recommended)
,儲存更改,然後按下面的操作去做:
1. 編譯工程;
2. 修正那些提示你需要新增 @objc 標誌的警告(請務必修正,不然即使編譯能過執行時也可能會出問題);
3. 修正 Xcode 提示的不需要新增 @objc 標誌的程式碼,持續構建和測試你的程式碼,直到沒有任何警告出現;
4. 開啟工程設定;
5. 選中 target,搜尋 `@objc` 找到 `Swift 3 @objc Inference` 選項,設為 `Default`。
複製程式碼
唔,以上這段大概是原文翻譯過來的了,官方文件原文如圖所示:
需要注意的是,因為我們這裡針對的並不是完整的 iOS 專案,而是 CocoaPods 庫,如果你的 OC Demo 沒有呼叫庫中需要暴露的功能(或者乾脆沒有 OC Demo),辣麼編譯器可能完全不會給你任何提示而是直接通過編譯了,直到你某一天在一個 OC 工程中引入這個庫才會發現並不能呼叫到某些方法或獲取某些屬性。
所以其實麻煩之處在於,編譯器並不會給你任何提示,因為編譯器也不知道哪些類 / 屬性 / 方法需要暴露,哪些需要被優化掉,需要開發人員自己決定並手動新增對應的 @objc
標誌,總結起來的話有以下幾點:
- 需要在 OC 中呼叫一個 Swift 4 的類,需要讓這個類繼承 NSObject 並且在這個類前加上 @objc 標誌;
- 需要在 OC 中呼叫一個 Swift 4 類的方法,需要在方法前加上 @objc 標誌(這裡有一個坑,如果是普通的函式呼叫還好,至少編譯器會報錯,如果是用
#selector
的方式呼叫的話,能過編譯並且在執行時直接找不到對應方法而閃退,建議升完 Swift 4 檢查一下所有的 #selector 呼叫); - 需要在 OC 中訪問一個 Swift 4 類的某個屬性,需要在屬性前加上 @objc 標誌(同上,如果是普通屬性訪問的話編譯器會報錯,但是 KVC 的話會在執行時找不到屬性而崩潰,記得檢查...);
- 需要在 OC 中訪問一個 Swift 4 類的擴充套件,只要在擴充套件前加上 @objc 標誌,該擴充套件的屬性和方法就都能被呼叫了。
4. 更新 Xcode 設定
- 如下圖所示,根據 Xcode 提示將工程設定進行更新,點選 Warning 後單擊
Perform Changes
按鈕即可;
- 檢查設定,將所有 target 的
Swift 3 @objc Inference
設定(如果有的話)改為Default
,之前改過的話就不用改了; - 搜尋
swift_ver
,可以看到當前的Swift Language Version
已經是Swift 4
了。
剩下少量方法名變動之類的更新大家可以根據提示自行修改,到這裡基本就完成了升級過程,不過先別急,接下來我們看注意事項。
二. 注意事項
以下情況必須要給對應的屬性或方法新增 @objc
標誌(當然,他們所在的類肯定也需要新增 @objc
標誌),不管是通過 OC 還是 Swift 呼叫:
- 使用
@selector()
或#selector()
方式呼叫的函式; - 使用 KVC 進行訪問的屬性;
- 使用 IBOutlet 或者 IBAction 和 StoryBoard 繫結的函式或屬性。
這些有部分在官方文件中也有提及:
三. 一些問題
- 同一工程的 Pods 庫是否可以既有 Swift 3 的也有 Swift 4 的?
Swift 的版本控制粒度在 framework 層面,也就是說同一個工程中不同的 framework 可以是按不同版本的 Swift 進行編譯的,所以並不需要等待專案依賴的所有 Pods 庫都支援 Swift 4 後再更新,完全可以將已經升級 Swift 4 的庫先用起來。
Swift 3 @objc Inference
選項是幹啥的?
在 Swift 4 之前,編譯器對 Objective-C 自動提供了一些 Swift 宣告。例如,編譯器會為 NSObject 子類的所有方法建立 Objective-C 入口點,該機制稱為 @objc 推斷(@objc Inference)。
在 Swift 4 中,這種自動的 @objc 推斷已被廢棄,因為生成所有這些 Objective-C 入口點有代價,會增大最終的二進位制檔案體積。當 Swift 3 @objc Inference
設定為 On
時,它會按照 Swift 4 之前的模式執行,不進行優化,也就是隱式為我們編寫的所有 Swift 程式碼提供 OC 入口。
但是,當設定為 On
時 Xcode 會報一個警告,建議修復這個警告,並將設定切換到 Default
。新的 Swift 專案的預設為“Default”。可以理解為該項設定為 On
時和上文程式碼轉換時選擇 Match Swift 3 Behavior
選項效果類似。
四. 沒了
升級完請務必跑一遍整體測試流程,暗坑無數,以防萬一,祝大家線上穩定。
如有任何智慧財產權、版權問題或理論錯誤,還請指正。
https://juejin.im/post/5a32180551882554b83790ca
轉載請註明原作者及以上資訊。