经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » iOS » 查看文章
iOS?Lotusoot模块化工具应用的动态思路
来源:jb51  时间:2022/8/1 19:09:39  对本文有异议

下文,写的是 Swift 依赖

OC 库,没有命名空间

组件化的要点-约定

个人觉得

例如,URL 路由的注册,就是把约定的信息,传过去。作为服务。

Lotusoot 包含服务调用,短链的注册与调用

下面着重讲服务调用,短链略

场景

project 有两个依赖 A (协议) 和 B (协议的实现者,提供服务)

project 引用 A,知道了协议信息

project 不引用 B , project 对 B 一无所知

这样 project 把 B 去掉,编译的更快

B 依赖 A, 引用 A, 实现 A 的协议,提供服务

调用服务

  1. // 拿到 key
  2. let lotus = s(AccountLotus.self)
  3. // kv 取得提供服务的实例
  4. let accountModule: AccountLotus = LotusootCoordinator.lotusoot(lotus: lotus) as! AccountLotus
  5. // 调用服务
  6. accountModule.login(username: "zhoulingyu", password: "wow") { (error) in
  7. print(error ?? "1234")
  8. }

第 3 步,调用服务很简单

第 2 步,挺精彩,充分使用了 Swift 编译时的静态特性

协议的方法,编译时确定了

需要几个参数,啥类型的,一般都可以显式使用

不用看到一个参数字典,啊,这是啥

  1. // 第 2 步。从下面取
  2. // 键值对,值是提供服务的对象
  3. var lotusootMap: Dictionary = Dictionary<String, Any>()

第 1 步, 拿到键

这里把协议名,作为 key

  1. // 使用泛型,取其描述
  2. // 协议,转协议名
  3. public extension String {
  4. init<Subject>(_ instance: Subject) {
  5. self.init(describing: instance)
  6. }
  7. }
  8. /// 通过 Subject 快速获取字符串
  9. public func s<Subject>(_ instance: Subject) -> String {
  10. return String(instance)
  11. }

注册服务

1, Project 没有 import B ( 提供服务 ), 怎么使用 B 的功能?

  1. public static func registerAll(serviceMap: Dictionary<String, String>) {
  2. for (lotus, lotusootName) in serviceMap {
  3. // lotus, 协议名
  4. // lotusootName, 包名.类名
  5. let classStringName = lotusootName
  6. // 反射,产生类
  7. // 提供服务的类,一定是 NSObject 的子类,拥有 init 方法 ( 这是个约定 )
  8. let classType = NSClassFromString(classStringName) as? NSObject.Type
  9. if let type = classType {
  10. // 产生对应的实例,强转为遵守协议的 ,即可
  11. let lotusoot = type.init()
  12. register(lotusoot: lotusoot, lotusName: lotus)
  13. }
  14. }
  15. }

2, 这里使用 python 脚本注册,编译的时候拿到信息 协议名:包名.类名

通过约定, 标记

  1. // @NameSpace(ZLYAccountModule)
  2. // @Lotusoot(AccountLotusoot)
  3. // @Lotus(AccountLotus)
  4. class AccountLotusoot: NSObject, AccountLotus {}

python 脚本找出标记,整合,放入 plist 文件中

1, 脚本入口

  1. lotusootSuffix = 'Lotusoot'
  2. length = len(sys.argv)
  3. if length != 3 and length != 4:
  4. print 'parameter error'
  5. os._exit(1)
  6. if length == 4:
  7. lotusootSuffix = sys.argv[3]
  8. lotusootFiles = findLotusoots(scanPath, lotusootSuffix + '.swift')
  9. else:
  10. // 走这里
  11. lotusootFiles = findAmbiguityLotusoots(scanPath)

翻阅每一个 swift 文件

  1. def findAmbiguityLotusoots(path):
  2. list = []
  3. for root, subFolders, files in os.walk(path):
  4. # Ignore 'Target Support Files' and 'Pods.xcodeproj'
  5. // 不需要处理的,不处理
  6. if 'Target Support Files' in subFolders:
  7. subFolders.remove('Target Support Files')
  8. // 不需要处理的,略
  9. if 'Pods.xcodeproj' in subFolders:
  10. subFolders.remove('Pods.xcodeproj')
  11. // 每一个文件
  12. for f in files:
  13. // 每一个 Swift 文件
  14. if f.endswith('.swift'):
  15. // 获取标记的配置
  16. tup = getLotusootConfig(os.path.join(root, f))
  17. if tup[0] and tup[1] and tup[2]:
  18. // 三者都满足,把文件路径,给添加了
  19. list.append(f)
  20. return list

扫描每一行,

获取配置,上面看到的包名,命名空间

@NameSpace(ZLYAccountModule)

上面看到的类名

@Lotusoot(AccountLotusoot)

上面看到的 key ( 协议名 )

@Lotus(AccountLotus)

  1. def getLotusootConfig(file):
  2. lotus = ''
  3. lotusoot = ''
  4. namespace = ''
  5. // 翻阅,文件的每一行
  6. for line in open(file):
  7. // 上面看到的 key
  8. if getLotus(line):
  9. lotus = getLotus(line)
  10. // 上面看到的类名
  11. if getLotusoot(line):
  12. lotusoot = getLotusoot(line)
  13. // 上面看到的包名,命名空间
  14. if getNameSpace(line):
  15. namespace = getNameSpace(line)
  16. return (lotus, lotusoot, namespace)

… 还有好多,

逻辑是获取配置,写入一个 plist

运行的时候,启动

  1. func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
  2. LotusootCoordinator.registerAll()
  3. return true
  4. }

注册就是,读取刚才写入的 plist, 作为 [协议名 : 包名.类名 ] 的字典,

  1. @objc public static func registerAll() {
  2. let lotusPlistPath = Bundle.main.path(forResource: "Lotusoot", ofType: "plist")
  3. if let lotusPlistPath = lotusPlistPath {
  4. let map = NSDictionary(contentsOfFile: lotusPlistPath)
  5. registerAll(serviceMap: map as! Dictionary<String, String>)
  6. }
  7. }

与上文,相呼应

动态思路

进入动态思路, 动态地注册 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 ( 放协议的 ), 添加一个协议

  1. public protocol Maid{
  2. var name: String{ get }
  3. }

module B 里面,必须有一个叫 key (协议) + C 的类

该类,遵守 Maid 协议。

通过协议属性,返回 B 中服务类的名称

  1. class AccountLotusC: NSObject, Maid{
  2. var name: String{
  3. return String(reflecting: AccountLotusoot.self)
  4. }
  5. }

这个过程,与上文模块化利用协议的设计,比较一致

约定是,实现协议的服务模块,

一定有一个 key + C 的类

提供服务类的名称

硬编码,比较轻微

代码实现

1、MachO 获取命名空间

  1. import MachO
  2. lazy var moduleNames: [String] = { () -> [String] in
  3. // 找到 project 名称,一会去除
  4. let mainNameTmp = NSStringFromClass(LotusootCoordinator.self)
  5. guard let mainName = mainNameTmp.components(separatedBy: ".").first else{
  6. fatalError("emptyMainProject")
  7. }
  8. var result = [String]()
  9. let cnt = _dyld_image_count()
  10. // 处理所有的包,系统的,用户的
  11. for i in 0..<cnt{
  12. if let tmp = _dyld_get_image_name(i){
  13. let name = String(validatingUTF8: tmp)
  14. // 系统的,不用管
  15. if let candidate = name, candidate.hasPrefix("/Users"){
  16. if let tmp = candidate.components(separatedBy: "/").last{
  17. // 去除 project 的
  18. if tmp != mainName{
  19. // 拿到用户依赖
  20. result.append(tmp)
  21. }
  22. }
  23. }
  24. }
  25. }
  26. return result
  27. }()

以上,模拟器 OK, 真机没试过 ( 手头没开发证书 )

2、包名+类名的验证

  1. @objc public static func lotusoot(lotus: String) -> Any? {
  2. // 已经缓存了
  3. if let val = sharedInstance.lotusootMap[lotus]{
  4. return val
  5. }
  6. else{
  7. var i = 0
  8. let names = LotusootCoordinator.sharedInstance.moduleNames
  9. let cnt = names.count
  10. // 遍历,用户包
  11. while i < cnt{
  12. // 按照约定,尝试制造助手类
  13. let classType = NSClassFromString(names[i] + "." + lotus + "C") as? NSObject.Type
  14. if let type = classType {
  15. // 实例化,助手类
  16. let assist = type.init()
  17. if let maid = assist as? Maid{
  18. // 拿到 module B 的服务类的名称
  19. let classType = NSClassFromString(maid.name) as? NSObject.Type
  20. if let type = classType {
  21. // 将 module B 的服务类,实例化
  22. let lotusoot = type.init()
  23. register(lotusoot: lotusoot, lotusName: lotus)
  24. }
  25. // 默认是,一个 module 一个服务类,
  26. // 排除掉,使用过的用户类
  27. LotusootCoordinator.sharedInstance.moduleNames.remove(at: i)
  28. break
  29. }
  30. }
  31. i+=1
  32. }
  33. if let val = sharedInstance.lotusootMap[lotus]{
  34. return val
  35. }
  36. else{
  37. fatalError("name Module of" + lotus)
  38. }
  39. }
  40. }

GitHub repo

到此这篇关于iOS Lotusoot模块化工具应用的动态思路的文章就介绍到这了,更多相关iOS Lotusoot模块化内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号