动态库实现极速编译

        这篇讲的是动态库链接器的实际应用,也就是编译调试的提速问题。

前言

编译中遇到的问题

        iOS 原生代码的编译调试,都是通过一遍又一遍地编译重启 App 来进行的。所以,项目代码量越大,编译时间就越长。

解决编译问题的办法

  • 将部分代码先编译成二进制集成到工程里,来避免每次都全量编译来加快编译速度(但每次编译都还是需要重启 App,需要再走一遍调试流程)

极速调试工具

Swift Playground

        Xcode 里集成的一个能够快速、实时调试程序的工具,可以实现所见即所得的效果。(可以去好好玩一玩)

Flutter Hot Reload

        Flutter 会在点击 reload 时去查看自上次编译以后改动过的代码,重新编译涉及到的代码库,还包括主库,以及主库的相关联库。所有这些重新编译过的库都会转换成内核文件发到 Dart VM 里,Dart VM 会重新加载新的内核文件,加载后会让 Flutter framework 触发所有的 Widgets 和 Render Objects 进行重建、重布局、重绘。

Injection for Xcode

使用方法

        作者已经开源了这个工具,地址是https://github.com/johnno1962/InjectionIII 。

        使用方式就是 clone 下代码,构建 InjectionPluginLite/InjectionPlugin.xcodeproj ;

        删除方式是,在终端里运行下面这行代码:

rm -rf ~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins/InjectionPlugin.xcplugin

        构建完成后,我们就可以编译项目。这时添加一个新的方法:

- (void)injected{ NSLog(@"I've been injected: %@", self);}

        然后在这个方法中添加一个断点,按下 ctrl + = ,接下来你会发现程序运行时会停到断点处,这样你的代码就成功地被运行中的 App 执行了。那么,Injection 是怎么做到的呢

原理

         Injection 会监听源代码文件的变化,如果文件被改动了,Injection Server 就会执行 rebuildClass 重新进行编译、打包成动态库,也就是 .dylib 文件。编译、打包成动态库后使用 writeSting 方法通过 Socket 通知运行的 App。

        Server 会在后台发送和监听 Socket 消息,实现逻辑在 InjectionServer.mm 的 runInBackground 方法里。Client 也会开启一个后台去发送和监听 Socket 消息,实现逻辑在 InjectionClient.mm里的 runInBackground 方法里。

        Client 接收到消息后会调用 inject(tmpfile: String) 方法,运行时进行类的动态替换。inject(tmpfile: String) 方法的代码大部分都是做新类动态替换旧类。inject(tmpfile: String) 的入参 tmpfile 是动态库的文件路径,那么这个动态库是如何加载到可执行文件里的呢?具体的实现在 inject(tmpfile: String) 方法开始里,如下:

        调用动态库加载函数 dlopen :

guard 
    let dl = dlopen("\(tmpfile).dylib", RTLD_NOW) 
else { 
    throw evalError("dlopen() error: \(String(cString: dlerror()))")
}

        如上代码所示,dlopen 会把 tmpfile 动态库文件载入运行的 App 里,返回指针 dl。接下来,dlsym 会得到 tmpfile 动态库的符号地址,然后就可以处理类的替换工作了。

        dlsym 调用对应代码如下:

guard 
    let newSymbol = dlsym(dl, info.dli_sname) 
else { 
    throw evalError("Could not locate newly loaded class symbol")
}

        当类的方法都被替换后,我们就可以开始重新绘制界面了。整个过程无需重新编译和重启 App,至此使用动态库方式极速调试的目的就达成了

        笔记:这里用到的socket是否就是进程间通信的websocket?

 需要注意的地方 

  • 1. 进行swizzling的类不要动态修改,否则二次交换,会引起死循环。
  • 2. 不支持方法的删除:删除后,方法调用仍然有效,不抛异常。
  • 3. 不支持新增类:新增类引入后使用无效。但类的重命名是有效的。
  • 4. 属性新增、删除、修改:反射上体现不出来。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值