Ideas dinámicas de iOS Lotusoot modular flexible

A continuación, se escriben las dependencias de Swift

Biblioteca OC, sin espacio de nombres

El punto de la componentización radica en el acuerdo.

Personalmente

Por ejemplo, el registro de enrutamiento de URL consiste en pasar la información acordada al pasado. como un servicio

Lotusoot incluye invocación de servicio, registro de cadena corta e invocación

Lo siguiente se centra en las llamadas de servicio y se omite la cadena corta.

una escena,

el proyecto tiene dos dependencias A (protocolo) y B (implementador del protocolo, proveedor del servicio)

proyecto se refiere a A y conoce la información del protocolo

proyecto no se refiere a B, proyecto no sabe nada acerca de B

  • De esta forma, el proyecto elimina B y compila más rápido.

B confía en A, hace referencia a A, implementa el protocolo de A y proporciona servicios

b, llamar al servicio

        // 拿到 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")

        }
复制代码
  • El tercer paso, llamar al servicio es muy sencillo

  • El paso 2, muy emocionante, aprovecha al máximo las características estáticas del tiempo de compilación de Swift

El método del protocolo, determinado en tiempo de compilación.

Necesita varios parámetros, qué tipo, generalmente se puede usar explícitamente

No veo un diccionario de parámetros, ah, ¿qué es esto?

// 第 2 步。从下面取

// 键值对,值是提供服务的对象

var lotusootMap: Dictionary = Dictionary<String, Any>()
复制代码
  • Paso 1, consigue las llaves

Aquí el nombre del protocolo se utiliza como clave.

// 使用泛型,取其描述

// 协议,转协议名

public extension String {

    init<Subject>(_ instance: Subject) {

        self.init(describing: instance)

    }

}



/// 通过 Subject 快速获取字符串

public func s<Subject>(_ instance: Subject) -> String {

    return String(instance)

}

复制代码

c, servicio de registro

1, el proyecto no importa B (proporciona servicios), ¿cómo usar la función de 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, aquí use el script de python para registrarse, obtenga la información al compilar协议名:包名.类名

por convención, marcado

// @NameSpace(ZLYAccountModule)

// @Lotusoot(AccountLotusoot)

// @Lotus(AccountLotus)

class AccountLotusoot: NSObject, AccountLotus {}
复制代码

secuencia de comandos de python para encontrar etiquetas, integrar, poner en plistarchivo

  • 1, entrada de guión

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)
复制代码
  • Ir a través de cada archivo rápido
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

复制代码

escanear cada línea,

Obtenga la configuración, el nombre del paquete que se ve arriba, el espacio de nombres

@NameSpace(ZLYAccountModule)

el nombre de la clase que se ve arriba

@Lotusoot(AccountLotusoot)

clave (nombre de protocolo) visto arriba

@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)
复制代码

... y muchos más,

La lógica es obtener la configuración y escribir un plist

Al correr, comienza

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

复制代码

El registro es para leer el plist recién escrito, como un diccionario de [nombre del protocolo: nombre del paquete.nombre de la clase],


@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>)

        }

    }

复制代码

se hace eco de lo anterior

Ingrese la idea dinámica y registre dinámicamente KV (nombre de protocolo: nombre de biblioteca de servicio. nombre de clase)

Hay una impresión arriba, si conoces la escena, puedes

Escribe el texto, cuando la escritura es larga,

¿Cómo reflejarme, 10 años de experiencia laboral?

No hay arriba, insiste en inventar el número de palabras.

1. El proyecto obtiene la clave (nombre del protocolo), sí
2. El proyecto obtiene toda la información de dependencia

por MachOlata

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

Supongo que te gusta

Origin juejin.im/post/7120295032606687245
Recomendado
Clasificación