我的编程空间,编程开发者的网络收藏夹
学习永远不晚

iOS Lotusoot模块化工具应用的动态思路

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

iOS Lotusoot模块化工具应用的动态思路

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

iOS Lotusoot模块化工具应用的动态思路

项目的不断更迭,导致项目越来越大,越来越臃肿,为了让项目更加条理,需要对项目进行模块化处理,为了减少模块之间的耦合,于是就有了Lotusoot这个工具
2022-11-13

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录