下文,写的是 Swift 依靠
OC 库,没有命名空间
组件化的要点,在于约好
个人觉得
例如,URL 路由的注册,便是把约好的信息,传过去。作为服务。
Lotusoot 包含服务调用,短链的注册与调用
下面着重讲服务调用,短链略
a, 场景,
project 有两个依靠 A (协议) 和 B (协议的完成者,供给服务)
project 引证 A,知道了协议信息
project 不引证 B , project 对 B 一窍不通
- 这样 project 把 B 去掉,编译的更快
B 依靠 A, 引证 A, 完成 A 的协议,供给服务
b, 调用服务
// 拿到 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")
}
-
第三步,调用服务很简单
-
第 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)
}
c, 注册服务
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
3.1 ,MachO
拿到所有依靠库的称号, 每一个 + “.” + key ( 协议 ) + Cls
= MachO
拿到所有依靠库的称号, 每一个 + “.” + module B 的类名
然后,看能不能实例化,
能够实例化,就 OK
试了一圈,不能够实例化,就报错
或许依靠 B, 没有添加
或许依靠 B 的类名,写错了
3.2 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)
}
}
}