接觸swift 已經有一年多的時間了,由最初的OC代碼轉爲 swift 代碼,然後從 swift 2.3 轉爲 swift 3。每次的轉換都感覺是將項目整個的翻新了一遍,每次的轉換代碼都是一次改朝換代。
以下是在代碼改朝換代的時候的一些心得:
在將 OC 代碼轉換爲 swift 代碼的時候,我當時使用的是 xcode7.3。xcode7.3在我的印象中,編寫OC代碼就是聯想功能最差的一個。 所以:
- 一、在更新swift的時候,在swift文件中,幾乎是不會聯想的,動則就是整個屏幕中的代碼全是白顏色,然後,類名,方法名,都是硬敲出來的。
- 二、既然是改寫 swift 代碼,那麼就是對swift 不是太瞭解
- 三、針對一些第三方庫,期望改爲swift版。例如:Masnory -> SnapKit
- 四、cocoapod 推薦使用 useasframework 的方式集成
- 五、在 swift 2.3 轉 swift 3.0 的時候,block(閉包)裏面的參數都不要形參,所以參數的前面都是要加上 _ 來防止錯誤。並且 block 非option 的都要加上 @escaping 來修飾
- 六、在swift中,在iOS8 機型中,所有的控制器在使用 xib 的情況下,都要對初始化方法 init(nibName:nibBundle)這個方法重寫,否則崩潰
- 七、增加橋接文件
以上幾點都是在轉碼的時候耗費時間比較長、存在坑的。下面說幾點在轉碼過程中總結的一些經驗
- 一、項目中所使用的到工具類、工廠類、公共類、網絡請求的封裝等等,就是指一些公共模塊。建議在轉碼初期,先將這些文件轉爲 swift。既然能稱爲工具類,那麼一般都是解耦的,所以說,可以新建一個swift項目,將這些工具類轉爲swift文件。這樣做的目的是: 一:工具類的使用量非常大,所以很有必要 swift2.0 / 3.0 化 二:在工具類使用如此頻繁的情況下,如果你的xcode不會聯想,這樣將會使多麼令人頭疼的事情啊!在這點印象頗深
- 二、更新第三方庫爲 swift 版,因爲雖說允許OC swift混編,但是在類型這個方面兼容性並不是太好。例如:OC中一些 NSArray 的地方,也許我們清楚裏面存放的是字符串,但是如果沒有顯示指定 NSArray <NSString* > * 的話,那麼在swift 中使用起來,你只能得到 Any 類型,使用的時候還要類型綁定,這個屬於類型兼容性。再者一點就是 OC 的方法在聯想方面差的要命
- 三、cocoapod 使用 useasframework,swift中比較注重 module 的概念,這個也是趨勢,所以同樣是混編,但是仍然要更改爲包的形式
- 四、關於block 形參的問題,這個需要我們有耐心的一個一個更改
- 五、在 swift 3.0 中返回值沒有使用那麼會報一個警告,添加一個@discardresult 在方法的前面,放置警告
- 六、針對第三點中的 包 的概念,我們會發現,例如在使用 snapkit 的時候,只要使用 snp 的地方都要 import SnapKit (當然這個主要是針對swift2.3 -> swift 3.0 並且沒有使用 useasframework )。這個時候會發現每個文件都 import SnapKit 這樣來一下,是多麼痛苦的事情。 下面是我當時新建的 mac 工程整個項目添加 import SnapKit 的方法
func importSnapKit(path: String) { let manager = FileManager.default guard let subPaths = try? manager.subpathsOfDirectory(atPath: path) else { return } for subPath in subPaths { let realPath = path + "/\(subPath)" var isDirectory: ObjCBool = true if manager.fileExists(atPath: realPath, isDirectory: &isDirectory) { if realPath.contains("SnapKit") { // 過濾自身 continue } if !isDirectory.boolValue { alterSnapKitContent(path: realPath) } else { importSnapKit(path: realPath) } } } } func alterSnapKitContent(path: String) { guard !path.contains("SnapKit") else { // 過濾自身 return } guard path.contains(".swift") else { return } guard var content = try? String.init(contentsOfFile: path) else { print("can't get content at path: \(path)") return } guard content.contains("snp.") || content.contains("make.") else { return } guard !content.contains("import SnapKit") else { return } let containUIKit = content.contains("import UIKit") let containFoundation = content.contains("import Foundation") if containUIKit { content = content.replacingOccurrences(of: "import UIKit", with: "import UIKit\n\nimport SnapKit") _ = try? content.write(toFile: path, atomically: true, encoding: String.Encoding.utf8) print("has add:\nimport SnapKit\nin file: \(path)") } else if containFoundation { content = content.replacingOccurrences(of: "import Foundation", with: "import Foundation\n\nimport SnapKit") _ = try? content.write(toFile: path, atomically: true, encoding: String.Encoding.utf8) print("has add:\nimport SnapKit\nin file: \(path)") } else { content = content.replacingOccurrences(of: "All rights reserved.\n//", with: "All rights reserved.\n//\n\nimport SnapKit") _ = try? content.write(toFile: path, atomically: true, encoding: String.Encoding.utf8) print("has replace all right reversed. ") } } // importSnapKit(path: "/Users/*/Desktop/projectname")
大致思路爲: 1、讀取項目中的每個文件,當然除了pod、snapkit 文件夾下面的 2、讀取每個文件中的內容,判斷是否包含snp. 這個字符串,如果存在,則需要導入 import SnapKit 。否則不需要 3、將 import SnapKit 放在 import UIKit 或 import Foundation 或 All rights reserved. 的下面一行 這樣等待半分鐘,將會自動在需要的文件中 import SnapKit
同樣:針對所有的 module 都可以這樣導入,只要將限制條件更改爲合適的即可
轉爲Swift 後:
現在我們公司都是使用swift 編程,swift在代碼編寫方面確實是能夠提高效率,尤其是swift 是面向協議編程,其靈活性不可言喻,並且在 swift 的強語言下,swift 項目是相當穩定的。目前 swift 項目唯一不足之處便是xcode 的編譯速度問題,編譯型語言。我們公司項目是比較大的,每次項目的編譯時間在15分鐘左右,接下來的任務就是如何降低編譯時間。
總體來說推薦大家轉爲swift編程。