iOS의 유연한 모듈식 Lotusoot의 동적 아이디어

아래에는 Swift 종속성이 작성되어 있습니다.

OC 라이브러리, 네임스페이스 없음

구성 요소화의 포인트는 계약에 있습니다.

몸소

예를 들어 URL 라우팅 등록은 합의된 정보를 과거로 전달하는 것입니다. 서비스로.

Lotusoot에는 서비스 호출, 짧은 체인 등록 및 호출이 포함됩니다.

다음은 서비스 호출에 중점을 두고 있으며 짧은 체인은 생략합니다.

에이, 장면,

프로젝트에는 A(프로토콜)와 B(프로토콜 구현, 서비스 제공)의 두 가지 종속성이 있습니다.

프로젝트는 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")

        }
复制代码
  • 세 번째 단계인 서비스 호출은 매우 간단합니다.

  • 매우 흥미로운 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
рекомендация