下文,写的是 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)
      }
    }
  }

GitHub repo