下文,写的是 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模块化内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!