iOS Lotusoot模塊化工具應用的動態思路
下文,寫的是 Swift 依賴
OC 庫,沒有命名空間
組件化的要點-約定
個人覺得
例如,URL 路由的註冊,就是把約定的信息,傳過去。作為服務。
Lotusoot 包含服務調用,短鏈的註冊與調用
下面著重講服務調用,短鏈略
場景
project 有兩個依賴 A (協議) 和 B (協議的實現者,提供服務)
project 引用 A,知道瞭協議信息
project 不引用 B , project 對 B 一無所知
這樣 project 把 B 去掉,編譯的更快
B 依賴 A, 引用 A, 實現 A 的協議,提供服務
調用服務
// 拿到 key let lotus = s(AccountLotus.self) // kv 取得提供服務的實例 let accountModule: AccountLotus = LotusootCoordinator.lotusoot(lotus: lotus) as! AccountLotus // 調用服務 accountModule.login(username: "zhoulingyu", password: "wow") { (error) in print(error ?? "1234") }
第 3 步,調用服務很簡單
第 2 步,挺精彩,充分使用瞭 Swift 編譯時的靜態特性
協議的方法,編譯時確定瞭
需要幾個參數,啥類型的,一般都可以顯式使用
不用看到一個參數字典,啊,這是啥
// 第 2 步。從下面取 // 鍵值對,值是提供服務的對象 var lotusootMap: Dictionary = Dictionary<String, Any>()
第 1 步, 拿到鍵
這裡把協議名,作為 key
// 使用泛型,取其描述 // 協議,轉協議名 public extension String { init<Subject>(_ instance: Subject) { self.init(describing: instance) } } /// 通過 Subject 快速獲取字符串 public func s<Subject>(_ instance: Subject) -> String { return String(instance) }
註冊服務
1, Project 沒有 import B ( 提供服務 ), 怎麼使用 B 的功能?
public static func registerAll(serviceMap: Dictionary<String, String>) { for (lotus, lotusootName) in serviceMap { // lotus, 協議名 // lotusootName, 包名.類名 let classStringName = lotusootName // 反射,產生類 // 提供服務的類,一定是 NSObject 的子類,擁有 init 方法 ( 這是個約定 ) let classType = NSClassFromString(classStringName) as? NSObject.Type if let type = classType { // 產生對應的實例,強轉為遵守協議的 ,即可 let lotusoot = type.init() register(lotusoot: lotusoot, lotusName: lotus) } } }
2, 這裡使用 python 腳本註冊,編譯的時候拿到信息 協議名:包名.類名
通過約定, 標記
// @NameSpace(ZLYAccountModule) // @Lotusoot(AccountLotusoot) // @Lotus(AccountLotus) class AccountLotusoot: NSObject, AccountLotus {}
python 腳本找出標記,整合,放入 plist
文件中
1, 腳本入口
lotusootSuffix = 'Lotusoot' length = len(sys.argv) if length != 3 and length != 4: print 'parameter error' os._exit(1) if length == 4: lotusootSuffix = sys.argv[3] lotusootFiles = findLotusoots(scanPath, lotusootSuffix + '.swift') else: // 走這裡 lotusootFiles = findAmbiguityLotusoots(scanPath)
翻閱每一個 swift 文件
def findAmbiguityLotusoots(path): list = [] for root, subFolders, files in os.walk(path): # Ignore 'Target Support Files' and 'Pods.xcodeproj' // 不需要處理的,不處理 if 'Target Support Files' in subFolders: subFolders.remove('Target Support Files') // 不需要處理的,略 if 'Pods.xcodeproj' in subFolders: subFolders.remove('Pods.xcodeproj') // 每一個文件 for f in files: // 每一個 Swift 文件 if f.endswith('.swift'): // 獲取標記的配置 tup = getLotusootConfig(os.path.join(root, f)) if tup[0] and tup[1] and tup[2]: // 三者都滿足,把文件路徑,給添加瞭 list.append(f) return list
掃描每一行,
獲取配置,上面看到的包名,命名空間
@NameSpace(ZLYAccountModule)
上面看到的類名
@Lotusoot(AccountLotusoot)
上面看到的 key ( 協議名 )
@Lotus(AccountLotus)
def getLotusootConfig(file): lotus = '' lotusoot = '' namespace = '' // 翻閱,文件的每一行 for line in open(file): // 上面看到的 key if getLotus(line): lotus = getLotus(line) // 上面看到的類名 if getLotusoot(line): lotusoot = getLotusoot(line) // 上面看到的包名,命名空間 if getNameSpace(line): namespace = getNameSpace(line) return (lotus, lotusoot, namespace)
… 還有好多,
邏輯是獲取配置,寫入一個 plist
運行的時候,啟動
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { LotusootCoordinator.registerAll() return true }
註冊就是,讀取剛才寫入的 plist, 作為 [協議名 : 包名.類名 ] 的字典,
@objc public static func registerAll() { let lotusPlistPath = Bundle.main.path(forResource: "Lotusoot", ofType: "plist") if let lotusPlistPath = lotusPlistPath { let map = NSDictionary(contentsOfFile: lotusPlistPath) registerAll(serviceMap: map as! Dictionary<String, String>) } }
與上文,相呼應
動態思路
進入動態思路, 動態地註冊 KV ( 協議名: 服務庫名.類名)
上文有一個印象,知道場景,即可
寫文,當寫長,
怎樣體現我,10 年工作經驗?
上文沒有,堅持湊字數
1,project 拿到 key (協議名) ,可以的
2,project 拿到所有的依賴信息
通過 MachO 可以
3,project 拿到服務類的名稱
確保 module B 的類名 = key ( 協議 ) + Cls
MachO
拿到所有依賴庫的名稱, 每一個 + “.” + key ( 協議 ) + Cls= MachO
拿到所有依賴庫的名稱, 每一個 + “.” + module B 的類名
然後,看能不能實例化,
能夠實例化,就 OK
試瞭一圈,不能夠實例化,就報錯
可能依賴 B, 沒有添加
可能依賴 B 的類名,寫錯瞭
project 拿到服務類的名稱, 優雅的
確保 module B 的類名 = key ( 協議 ) + Cls,
硬編碼,是不好的
依賴 A ( 放協議的 ), 添加一個協議
public protocol Maid{ var name: String{ get } }
module B 裡面,必須有一個叫 key (協議) + C 的類
該類,遵守 Maid
協議。
通過協議屬性,返回 B 中服務類的名稱
class AccountLotusC: NSObject, Maid{ var name: String{ return String(reflecting: AccountLotusoot.self) } }
這個過程,與上文模塊化利用協議的設計,比較一致
約定是,實現協議的服務模塊,
一定有一個 key + C 的類
提供服務類的名稱
硬編碼,比較輕微
代碼實現
1、MachO 獲取命名空間
import MachO lazy var moduleNames: [String] = { () -> [String] in // 找到 project 名稱,一會去除 let mainNameTmp = NSStringFromClass(LotusootCoordinator.self) guard let mainName = mainNameTmp.components(separatedBy: ".").first else{ fatalError("emptyMainProject") } var result = [String]() let cnt = _dyld_image_count() // 處理所有的包,系統的,用戶的 for i in 0..<cnt{ if let tmp = _dyld_get_image_name(i){ let name = String(validatingUTF8: tmp) // 系統的,不用管 if let candidate = name, candidate.hasPrefix("/Users"){ if let tmp = candidate.components(separatedBy: "/").last{ // 去除 project 的 if tmp != mainName{ // 拿到用戶依賴 result.append(tmp) } } } } } return result }()
以上,模擬器 OK, 真機沒試過 ( 手頭沒開發證書 )
2、包名+類名的驗證
@objc public static func lotusoot(lotus: String) -> Any? { // 已經緩存瞭 if let val = sharedInstance.lotusootMap[lotus]{ return val } else{ var i = 0 let names = LotusootCoordinator.sharedInstance.moduleNames let cnt = names.count // 遍歷,用戶包 while i < cnt{ // 按照約定,嘗試制造助手類 let classType = NSClassFromString(names[i] + "." + lotus + "C") as? NSObject.Type if let type = classType { // 實例化,助手類 let assist = type.init() if let maid = assist as? Maid{ // 拿到 module B 的服務類的名稱 let classType = NSClassFromString(maid.name) as? NSObject.Type if let type = classType { // 將 module B 的服務類,實例化 let lotusoot = type.init() register(lotusoot: lotusoot, lotusName: lotus) } // 默認是,一個 module 一個服務類, // 排除掉,使用過的用戶類 LotusootCoordinator.sharedInstance.moduleNames.remove(at: i) break } } i+=1 } if let val = sharedInstance.lotusootMap[lotus]{ return val } else{ fatalError("name Module of" + lotus) } } }
GitHub repo
到此這篇關於iOS Lotusoot模塊化工具應用的動態思路的文章就介紹到這瞭,更多相關iOS Lotusoot模塊化內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Xcode中代碼註釋編寫的一些小技巧
- Flutter iOS開發OC混編Swift動態庫和靜態庫問題填坑
- IntelliJ IDEA中Project與Module的概念以及區別
- Swift如何優雅的進行解包
- Swift代碼自定義UIView實現示例