iOSの柔軟なモジュラーLotusootの動的なアイデア

以下に、Swiftの依存関係を記述します

OCライブラリ、名前空間なし

コンポーネント化のポイントは合意にあります

個人的に

たとえば、URLルーティングの登録は、合意された情報を過去に渡すことです。サービスとして。

Lotusootには、サービスの呼び出し、ショートチェーンの登録、および呼び出しが含まれます

以下はサービスコールに焦点を当てており、ショートチェーンは省略されています。

風景、

プロジェクトには、A(プロトコル)とB(プロトコルの実装者、サービスの提供)の2つの依存関係があります

プロジェクトはAを参照し、プロトコル情報を知っています

プロジェクトはBを参照していません、プロジェクトはBについて何も知りません

  • このようにして、プロジェクトは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")

        }
复制代码
  • 3番目のステップ、サービスの呼び出しは非常に簡単です

  • ステップ2は非常にエキサイティングで、Swiftコンパイル時の静的機能を最大限に活用します

コンパイル時に決定されるプロトコルのメソッド

いくつかのパラメータが必要です。どのタイプで、一般的に明示的に使用できます。

パラメータ辞書が表示されない、ああ、これは何ですか

// 第 2 步。从下面取

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

var lotusootMap: Dictionary = Dictionary<String, Any>()
复制代码
  • ステップ1、キーを取得します

ここでは、プロトコル名がキーとして使用されています

// 使用泛型,取其描述

// 协议,转协议名

public extension String {

    init<Subject>(_ instance: Subject) {

        self.init(describing: instance)

    }

}



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

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

    return String(instance)

}

复制代码

c、登録サービス

1、プロジェクトは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 {}
复制代码

タグを見つけ、統合し、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)
复制代码
  • すべての迅速なファイルを確認します
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)

上に表示されているキー(プロトコル名)

@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.プロジェクトはキー(プロトコル名)を取得します、はい
2.プロジェクトはすべての依存関係情報を取得します

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

おすすめ

転載: juejin.im/post/7120295032606687245