前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Injection for Xcode 高效Xcode编译调试插件

Injection for Xcode 高效Xcode编译调试插件

作者头像
freesan44
发布2018-09-05 15:56:19
1.8K0
发布2018-09-05 15:56:19
举报
文章被收录于专栏:freesan44freesan44

github上的开源项目,Xcode插件。

对于iOS开发者来说,XCode有个另人十分难耐的特性——编译时长的问题。也许工作的时候你能够为自己找到一个闲下来喝杯咖啡的正当的借口,然而,多次的调试编译过程足以让你喝上好多杯咖啡了。应该说,Injection是iOS开发者的福音,它在很大程度上优化了XCode的性能,提升了开发者的工作效率。

Injection能够在app运行时动态地向Swift或者OC文件注入新代码并且即时地呈现在运行中的模拟器的app上,从而达到提高程序编译速度,提高开发效率的目的。开发者不需要重新编译重新运行整个项目,这样的优化使得编译周期从7秒缩短至1秒。从XCode的输出台来看,每次在进行代码注入之后都只会编译被注入了代码的文件。这么一听有点类似于增量编译。

设想这样一个场景,对于一个编译启动需要10分钟的项目,如果你想对某个功能的动画效果进行微调,是否意味着你需要以至少10分钟为一个调试周期去对你的改动进行测试,而injection则能够在程序运行时动态的改动方法实现,并呈现在模拟器或真机上。

Injection Github https://github.com/johnno1962/injectionforxcode ; 或者到这里去看看他的演示:https://www.youtube.com/watch?v=uftvtmyZ8TM

对于Injection的安装使用,可以到第一个链接里下载package。Injection团队为开发者准备了一套傻瓜式的配置流程,基本上都是单击continue就行了,然后重启你的Xcode。装成功后你会看到product > injection plugin。此时你应该已经装成功了。点击 Product >Injection Plugin > Patch Project for Injection 选项, 之后插件会在main.m 中插入两段代码。

C代码 收藏代码

ifdef DEBUG

static char _inMainFilePath[] = FILE; static const char *_inIPAddresses[] = {"10.12.1.67", "127.0.0.1", 0};

define INJECTION_ENABLED

import "/tmp/injectionforxcode/BundleInjection.h"

endif

这不会影响程序原有代码,如果要还原,随时可以通过点击 Revert Injection’s Changes 选项来还原。你可以开搞了。

用一个demo做实验,将project运行起来,在运行时对你的代码进行改动,可以使用快捷键Ctrl + =快速运行。也可以在 product > injection plugin > inject and reset app。 你会发现你改动的代码所在类的左上角有一个蓝色的进度条,一秒不到的时间就能够完成注入并运行在你的app上。当然,你也能够在你改动的代码的方法里边加上一个断点,快捷键Ctrl + = ,你会发现运行时会停在你设定的breakpoint上。

对于Swift文件injection好像还不能做到完美支持,github上有相关的解释,我还没有深入的尝试,有兴趣的童鞋可以去看看,顺便交流交流。 injection是Xcode IDE的一个扩展,允许你去对类的一个方法实现打补丁而不需要重启app。官方的原理如下:

It performs this by parsing the build logs of the application to determine how a source file was last compiled. With this it wraps the result of re-compiling into a bundle which is injected into the application using the dynamic loader. At this point there are two versions of a class in the app, the original and a new modified version from the bundle. The modified version is then “swizzled” onto the original class so changes take effect. (个人翻译)它通过解析程序的编译日志来确定最后一次编译的源文件。通过动态加载程序把重新编译的结果打包到被注入代码的app中。此时有两个版本的类应用,最初的和一个新的修改版本的包。这个修改后的版本,被“swizzled到”原始类中而生效。

除此之外,injection插件还有一个参数调节器Tunable Parameters,对于UI开发来说是个利器。比如对颜色的确定,对字体大小的界定等等。运行app,然后对参数进行修改就能够动态的进行调试了。直观而且方便。 这里写图片描述 对于 Tunable Parameters的使用我还没有涉足,它的使用目前仅限于Swift项目,还需要在项目中进行一些诸如添加头部代码的配置,有兴趣的童鞋可以到这里了解: https://github.com/johnno1962/injectionforxcode/blob/master/documentation/tunable_parameters.md

其实也不复杂,就是在新建一个main.m文件之后加上几行代码。 在使用injection时,一个新的Xcode项目文件将会在原本项目的文件里生成(iOSInjectionProject或OSXInjectionProject)。这个文件是用于存放那些被injecte的项目文件的,建议将其加入到.gitignore 中,直接忽略。

每一次的项目文件被injected,在injection项目目录里的injectionCount.txt中的数字就会增加。它可以很直观的告诉你通过injection进行了多少的文件改动。

如果你想在真机或Appcode上进行测试: 你需要做一些轻量级的配置:在你的main.m文件加上如下几行代码:

C代码 收藏代码

ifdef DEBUG

static char _inMainFilePath[] = FILE; static const char *_inIPAddresses[] = {"10.12.1.67", "127.0.0.1", 0};

define INJECTION_ENABLED

import "/tmp/injectionforxcode/BundleInjection.h"

endif

这个配置也可以通过Product > Injection Plugin > Patch Project For Injection 来进行自动配置。对于Swift文件,你需要添加一个空的main.m文件来完成配置。

至于使用Appcode的盆友,可以上github上看看教程: https://github.com/johnno1962/injectionforxcode

14.2 Limitations of Injection(局限性)

贴github原文:

There are limitations of course, largely centering around static variables, static or global functions and their Swift equivalents. Consider the following Objective-C code. 这里写图片描述

One potential problem is when the new version of the class is loaded, it comes with it’s own versions of static variables such as sharedInstance and the once token. After injection has occurred, this would generate a new singleton instance. To prevent this, class methods with the prefix “shared” are not swizzled on injection to support this common idiom.

It can be tough to look through all of the memory of a running application. In order to determine the classes and instances to call the injected callbacks on, Injection performs a “sweep” to find all objects in memory. Roughly, this involves looking at an object, then recursively looking through objects which it refers to. For example, the object’s instance variables and properties.

This process is seeded using the application’s delegate and all windows. Once all the in-memory reference are collected, Injection will then filter these references to ones that it has compiled and injected. Then sending them the messages referenced in the callbacks section.

If no references are found, Injection will look through all objects that are referred to via sharedInstance. If that fails, well, Injection couldn’t find your instance. This is one way in which you may miss callbacks in your app. The function dispatch_on_main does not inject, as it has been statically linked into the application. It does however, inject by proxy in the case shown via the doSomething method. dispatch_on_main will have been linked locally to a version in the object file being injected. injection作为Xcode的插件,还是有局限性的。 injection的作用域主要集中在静态变量、静态或全局函数及其Swift的当量(按:Swift equivalents)。

以下是作者贴的示例代码: 这里写图片描述 1)有一个潜在的问题,当类的新版本被加载,它带有自己的静态变量版本sharedInstance和once标记。发生injected后,将产生一个新的单一实例。

To prevent this, class methods with the prefix "shared" are not swizzled on injection to support this common idiom. 以上这句我捉摸了很久还是没有吃透。 2)它可以浏览所有的正在运行的应用程序的内存。为了确定类和实例能够调用injectied的回调,injection会执行一次“扫描”,找到在内存中的所有对象。粗略说,这涉及了一个对象,然后通过递归寻找它所指向的对象。例如对象的实例变量和内容(properties)。

3)This process is seeded using the application’s delegate and all windows.(按:这个过程通过应用程序的代理和所有的窗口)。一旦所有在内存中的引用被收集,injection将会过滤这些它已经编译和injected的引用,。然后再将被引用信息的回调部分发送出去。

4)如果没有找到引用注入的内容,Injection将通过sharedInstance查找所有被涉及到的对象。如果没有找到任何对象,那么,Injection将找不到你的实例。这会导致你无法在你的app中进行回调函数的调用。

5)函数dispatch_on_main无法被injected,因为它已被静态地链接到应用程序。但是,injection可以通过代码示例里的doSomething方法进行inject。dispatch_on_main将会被链接到本地的在被injected对象文件的一个新版本中。

以上内容参考:https://github.com/johnno1962/injectionforxcode

对于某些童鞋的疑问:injection的编译效率与XCode自身的增量编译有什么优势?我已经在github上Issue了作者并得到了如下回复:

这里写图片描述 但是具体到底能够提升多少,这个有待进一步的测试。 github上的开源项目,Xcode插件。

对于iOS开发者来说,XCode有个另人十分难耐的特性——编译时长的问题。也许工作的时候你能够为自己找到一个闲下来喝杯咖啡的正当的借口,然而,多次的调试编译过程足以让你喝上好多杯咖啡了。应该说,Injection是iOS开发者的福音,它在很大程度上优化了XCode的性能,提升了开发者的工作效率。

Injection能够在app运行时动态地向Swift或者OC文件注入新代码并且即时地呈现在运行中的模拟器的app上,从而达到提高程序编译速度,提高开发效率的目的。开发者不需要重新编译重新运行整个项目,这样的优化使得编译周期从7秒缩短至1秒。从XCode的输出台来看,每次在进行代码注入之后都只会编译被注入了代码的文件。这么一听有点类似于增量编译。

设想这样一个场景,对于一个编译启动需要10分钟的项目,如果你想对某个功能的动画效果进行微调,是否意味着你需要以至少10分钟为一个调试周期去对你的改动进行测试,而injection则能够在程序运行时动态的改动方法实现,并呈现在模拟器或真机上。

Injection Github https://github.com/johnno1962/injectionforxcode ; 或者到这里去看看他的演示:https://www.youtube.com/watch?v=uftvtmyZ8TM

对于Injection的安装使用,可以到第一个链接里下载package。Injection团队为开发者准备了一套傻瓜式的配置流程,基本上都是单击continue就行了,然后重启你的Xcode。装成功后你会看到product > injection plugin。此时你应该已经装成功了。点击 Product >Injection Plugin > Patch Project for Injection 选项, 之后插件会在main.m 中插入两段代码。

C代码

<embed wmode="transparent" src="http://815222418.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=%23ifdef%20DEBUG%0Astatic%20char%20_inMainFilePath%5B%5D%20%3D%20__FILE__%3B%0Astatic%20const%20char%20*_inIPAddresses%5B%5D%20%3D%20%7B%2210.12.1.67%22%2C%20%22127.0.0.1%22%2C%200%7D%3B%0A%0A%23define%20INJECTION_ENABLED%0A%23import%20%22%2Ftmp%2Finjectionforxcode%2FBundleInjection.h%22%0A%23endif" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" height="15" width="14"> [

收藏代码

](javascript:void() "收藏这段代码")

  1. ifdef DEBUG
  2. static char _inMainFilePath[] = FILE;
  3. static const char *_inIPAddresses[] = { "10.12.1.67" , "127.0.0.1" , 0};
  4. define INJECTION_ENABLED
  5. import "/tmp/injectionforxcode/BundleInjection.h"
  6. endif

这不会影响程序原有代码,如果要还原,随时可以通过点击 Revert Injection’s Changes 选项来还原。你可以开搞了。

用一个demo做实验,将project运行起来,在运行时对你的代码进行改动,可以使用快捷键Ctrl + =快速运行。也可以在 product > injection plugin > inject and reset app。 你会发现你改动的代码所在类的左上角有一个蓝色的进度条,一秒不到的时间就能够完成注入并运行在你的app上。当然,你也能够在你改动的代码的方法里边加上一个断点,快捷键Ctrl + = ,你会发现运行时会停在你设定的breakpoint上。

对于Swift文件injection好像还不能做到完美支持,github上有相关的解释,我还没有深入的尝试,有兴趣的童鞋可以去看看,顺便交流交流。 injection是Xcode IDE的一个扩展,允许你去对类的一个方法实现打补丁而不需要重启app。官方的原理如下:

It performs this by parsing the build logs of the application to determine how a source file was last compiled. With this it wraps the result of re-compiling into a bundle which is injected into the application using the dynamic loader. At this point there are two versions of a class in the app, the original and a new modified version from the bundle. The modified version is then “swizzled” onto the original class so changes take effect.

(个人翻译)它通过解析程序的编译日志来确定最后一次编译的源文件。通过动态加载程序把重新编译的结果打包到被注入代码的app中。此时有两个版本的类应用,最初的和一个新的修改版本的包。这个修改后的版本,被“swizzled到”原始类中而生效。

除此之外,injection插件还有一个参数调节器Tunable Parameters,对于UI开发来说是个利器。比如对颜色的确定,对字体大小的界定等等。运行app,然后对参数进行修改就能够动态的进行调试了。直观而且方便。

这里写图片描述

对于 Tunable Parameters的使用我还没有涉足,它的使用目前仅限于Swift项目,还需要在项目中进行一些诸如添加头部代码的配置,有兴趣的童鞋可以到这里了解: https://github.com/johnno1962/injectionforxcode/blob/master/documentation/tunable_parameters.md

其实也不复杂,就是在新建一个main.m文件之后加上几行代码。 在使用injection时,一个新的Xcode项目文件将会在原本项目的文件里生成(iOSInjectionProject或OSXInjectionProject)。这个文件是用于存放那些被injecte的项目文件的,建议将其加入到.gitignore 中,直接忽略。

每一次的项目文件被injected,在injection项目目录里的injectionCount.txt中的数字就会增加。它可以很直观的告诉你通过injection进行了多少的文件改动。

如果你想在真机或Appcode上进行测试: 你需要做一些轻量级的配置:在你的main.m文件加上如下几行代码:

C代码

<embed wmode="transparent" src="http://815222418.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=%23ifdef%20DEBUG%0Astatic%20char%20_inMainFilePath%5B%5D%20%3D%20__FILE__%3B%0Astatic%20const%20char%20*_inIPAddresses%5B%5D%20%3D%20%7B%2210.12.1.67%22%2C%20%22127.0.0.1%22%2C%200%7D%3B%0A%0A%23define%20INJECTION_ENABLED%0A%23import%20%22%2Ftmp%2Finjectionforxcode%2FBundleInjection.h%22%0A%23endif" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" height="15" width="14"> [

收藏代码

](javascript:void() "收藏这段代码")

  1. ifdef DEBUG
  2. static char _inMainFilePath[] = FILE;
  3. static const char *_inIPAddresses[] = { "10.12.1.67" , "127.0.0.1" , 0};
  4. define INJECTION_ENABLED
  5. import "/tmp/injectionforxcode/BundleInjection.h"
  6. endif

这个配置也可以通过Product > Injection Plugin > Patch Project For Injection 来进行自动配置。对于Swift文件,你需要添加一个空的main.m文件来完成配置。

至于使用Appcode的盆友,可以上github上看看教程: https://github.com/johnno1962/injectionforxcode


14.2 Limitations of Injection(局限性)

贴github原文:

There are limitations of course, largely centering around static variables, static or global functions and their Swift equivalents. Consider the following Objective-C code.

这里写图片描述

  • One potential problem is when the new version of the class is loaded, it comes with it’s own versions of static variables such as sharedInstance and the once token. After injection has occurred, this would generate a new singleton instance.

To prevent this, class methods with the prefix “shared” are not swizzled on injection to support this common idiom.

  • It can be tough to look through all of the memory of a running application. In order to determine the classes and instances to call the injected callbacks on, Injection performs a “sweep” to find all objects in memory. Roughly, this involves looking at an object, then recursively looking through objects which it refers to. For example, the object’s instance variables and properties.

This process is seeded using the application’s delegate and all windows. Once all the in-memory reference are collected, Injection will then filter these references to ones that it has compiled and injected. Then sending them the messages referenced in the callbacks section. If no references are found, Injection will look through all objects that are referred to via sharedInstance. If that fails, well, Injection couldn’t find your instance. This is one way in which you may miss callbacks in your app.

  • The function dispatch_on_main does not inject, as it has been statically linked into the application. It does however, inject by proxy in the case shown via the doSomething method. dispatch_on_main will have been linked locally to a version in the object file being injected.

injection作为Xcode的插件,还是有局限性的。 injection的作用域主要集中在静态变量、静态或全局函数及其Swift的当量(按:Swift equivalents)。

以下是作者贴的示例代码:

这里写图片描述

1)有一个潜在的问题,当类的新版本被加载,它带有自己的静态变量版本sharedInstance和once标记。发生injected后,将产生一个新的单一实例。

代码语言:javascript
复制
  To prevent this, class methods with the prefix "shared" are not swizzled on 
injection to support this common idiom.       
  以上这句我捉摸了很久还是没有吃透。

2)它可以浏览所有的正在运行的应用程序的内存。为了确定类和实例能够调用injectied的回调,injection会执行一次“扫描”,找到在内存中的所有对象。粗略说,这涉及了一个对象,然后通过递归寻找它所指向的对象。例如对象的实例变量和内容(properties)。

3)This process is seeded using the application’s delegate and all windows.(按:这个过程通过应用程序的代理和所有的窗口)。一旦所有在内存中的引用被收集,injection将会过滤这些它已经编译和injected的引用,。然后再将被引用信息的回调部分发送出去。

4)如果没有找到引用注入的内容,Injection将通过sharedInstance查找所有被涉及到的对象。如果没有找到任何对象,那么,Injection将找不到你的实例。这会导致你无法在你的app中进行回调函数的调用。

5)函数dispatch_on_main无法被injected,因为它已被静态地链接到应用程序。但是,injection可以通过代码示例里的doSomething方法进行inject。dispatch_on_main将会被链接到本地的在被injected对象文件的一个新版本中。

以上内容参考:https://github.com/johnno1962/injectionforxcode

对于某些童鞋的疑问:injection的编译效率与XCode自身的增量编译有什么优势?我已经在github上Issue了作者并得到了如下回复:

这里写图片描述

但是具体到底能够提升多少,这个有待进一步的测试。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017.11.29 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ifdef DEBUG
  • define INJECTION_ENABLED
  • import "/tmp/injectionforxcode/BundleInjection.h"
  • endif
  • ifdef DEBUG
  • define INJECTION_ENABLED
  • import "/tmp/injectionforxcode/BundleInjection.h"
  • endif
    • 14.2 Limitations of Injection(局限性)
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档