1. Mac的App Store上下載安裝InjectionIII.
1891642388398_.pic.jpg
2. 打開(kāi)InjectionIII, Open Project, 選擇你的項(xiàng)目目錄.
1911642388527_.pic.jpg
3. 選擇的項(xiàng)目會(huì)在Open Recent中出現(xiàn), 保持File Watcher的選項(xiàng)勾選.
支持選擇多個(gè)項(xiàng)目。切換項(xiàng)目的同時(shí)必須去Open Recent再次點(diǎn)擊,文件順序會(huì)隨之變化
1931642388629_.pic.jpg
4. 在AppDelegate的DidFinishLaunchingWithOptions配置InjectionIII的路徑
/// Object -C
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
#ifdef DEBUG
//InjectionIII 注入
[[NSBundle bundleWithPath:@"/Applications/InjectionIII.app/Contents/Resources/iOSInjection.bundle"] load];
#else
#endif
return YES;
}
/// Swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/iOSInjection.bundle")?.load()
return true
}
5. 在需要?jiǎng)討B(tài)調(diào)試的頁(yè)面控制器中寫上injected方法, 把需要操作的UI方法添加到injected中執(zhí)行
- injected 方法內(nèi)可以調(diào)用view的生命周期或者視圖初始化和 其他操作扁瓢,cmd + s 即可看到效果
// Objective-C:
- (void)injected {
#ifdef DEBUG
NSLog(@"I've been injected: %@", self);
self.view.backColor = xxxxx;
#endif
}
// Swift
@objc func injected() {
#if DEBUG
print("I've been injected: \(self)")
self.view.backColor = xxxxx
#endif
}
6. 重新編譯項(xiàng)目, 控制臺(tái)可以看到
- 下面的只是警告, 作者在Issue中已經(jīng)解釋, 不耽誤正常使用.
先在Xcode Command+R運(yùn)行一下你的項(xiàng)目了赵,控制臺(tái)提示如下信息旧烧,即表示InjectionIII連接成功,連接成功的話InjectionIII標(biāo)志會(huì)由藍(lán)變橙(可能不同InjectionIII版本顏色不太一樣)。
1941642389193_.pic.jpg
7. 修改完UI, 直接cmd + S就能看到效果, 部分頁(yè)面可能耗時(shí)比較久或無(wú)法使用, 正常頁(yè)面均能使用.
8. 注意點(diǎn):
- InjectionIII工具只對(duì)模擬器有效,真機(jī)無(wú)效牛柒。
- 在injected方法里面修改的代碼堪簿,再次Command+R運(yùn)行時(shí)不會(huì)起作用。因?yàn)榇a里面只聲明了injected方法皮壁,并沒(méi)有去調(diào)用椭更,所以不會(huì)生效;我們Command+S保存代碼后生效蛾魄,是因?yàn)樵诒4婧笸ㄟ^(guò)InjectionIII工具讀取injected方法里面修改的代碼虑瀑,“告訴”模擬器哪兒修改了,是InjectionIII起的作用滴须。因此我們需要把修改的代碼放到合適的位置舌狗,讓程序一運(yùn)行就會(huì)執(zhí)行。
9. Flutter Hot Reload介紹
- Flutter是Google開(kāi)發(fā)的一個(gè)跨平臺(tái)開(kāi)發(fā)框架, 調(diào)試也是快速實(shí)時(shí)的.
- 在Flutter編輯器中修改文字代碼后, 點(diǎn)擊reload, App不用重啟, 模擬器的內(nèi)容就會(huì)立刻改變.
Flutter實(shí)現(xiàn)實(shí)時(shí)編譯的原理
- Flutter會(huì)在點(diǎn)擊reload時(shí)取查看上次編譯以后改動(dòng)過(guò)的代碼, 重新編譯涉及到的代碼庫(kù).
- 重新編譯過(guò)的庫(kù)會(huì)轉(zhuǎn)換成內(nèi)核文件發(fā)到Dart VM里, DartVM會(huì)重新加載新的內(nèi)核文件,
- 加載后會(huì)讓Flutter framework觸發(fā)所有的Widgets和Render Objects進(jìn)行重建描馅、重布局把夸、重繪.
- Flutter為了能夠支持跨平臺(tái)開(kāi)發(fā), 使用了自研的Dart語(yǔ)言配合在App內(nèi)集成Dart VM的方式運(yùn)行Flutter程序.
10. iOS原生項(xiàng)目擁有Flutter熱重載的原理
- Injection工具可以動(dòng)態(tài)地將iOS代碼在已運(yùn)行的程序中執(zhí)行, 不用重啟.
- Injection會(huì)監(jiān)聽(tīng)源代碼文件的變化, 如果文件被改動(dòng)了,
- Injection Server就會(huì)執(zhí)行rebuildClass重新進(jìn)行編譯而线、打包成動(dòng)態(tài)庫(kù).dylib文件,
- 編譯铭污、打包成動(dòng)態(tài)庫(kù)后, 使用writeString方法通過(guò)Socket通知運(yùn)行的App.
- (BOOL)writeString:(NSString *)string {
const char *utf8 = string.UTF8String;
uint32_t length = (uint32_t)strlen(utf8);
if (write(clientSocket, &length, sizeof length) != sizeof length ||
write(clientSocket, utf8, length) != length)
return FALSE;
return TRUE;
}
- Server會(huì)在后臺(tái)發(fā)送和監(jiān)聽(tīng)Socket消息, Client也會(huì)開(kāi)啟一個(gè)后臺(tái)去發(fā)送和監(jiān)聽(tīng)Socket消息.
- Client接收到消息后會(huì)調(diào)用inject(tmpfile: String)方法, 運(yùn)行時(shí)進(jìn)行類的動(dòng)態(tài)替換(新類動(dòng)態(tài)替換舊類).
- dlopen會(huì)把tmpfile動(dòng)態(tài)庫(kù)文件載入運(yùn)行的App里, 返回指針dl.
- 接下來(lái), dlsym會(huì)得到tmpfile動(dòng)態(tài)庫(kù)的符號(hào)地址, 然后就可以處理類的替換工作了.
- 當(dāng)類的方法都被替換后, 我們就可以開(kāi)始重新繪制界面了.
使用動(dòng)態(tài)庫(kù)方式極速調(diào)試, 整個(gè)過(guò)程無(wú)需重新編譯和重啟App.
a6533a7dca644f8792d5fef0c05bcd3f~tplv-k3u1fbpfcp-watermark.image.png