中文读数:中文集成指南
代码注入允许您在iOS模拟器中逐步更新功能的实现以及类,结构或枚举的任何方法,而无需执行完整的重建或重新启动您的应用程序。这为开发人员节省了大量的时间调整代码或在设计上迭代。有效地,它将XCode从“源编辑器”更改为“程序编辑器” ,其中源更改不仅将源更改保存到磁盘,而且直接将其直接放入您的运行程序中。
现在,将您的项目设置为使用注射,就像下载应用程序的GitHub版本之一或从Mac App Store中下载并在应用程序中的某个地方添加代码要在启动时执行(不再需要实际运行应用程序本身)。
#if DEBUG
Bundle ( path : " /Applications/InjectionIII.app/Contents/Resources/iOSInjection.bundle " ) ? . load ( )
//for tvOS:
Bundle ( path : " /Applications/InjectionIII.app/Contents/Resources/tvOSInjection.bundle " ) ? . load ( )
//Or for macOS:
Bundle ( path : " /Applications/InjectionIII.app/Contents/Resources/macOSInjection.bundle " ) ? . load ( )
#endif添加选项-Xlinker和-interposable (无需双引号,在单独的行上)将选项添加到项目中目标(仅用于Debug配置)的“其他链接器标志”中,以启用“插入”(请参见下面的说明)。

之后,当您在模拟器中运行应用程序时,您应该看到一条消息,说明观察者已经为您的主目录启动了文件,并且每当您将源文件保存在当前项目中时,都应该报告它已被注入。这意味着所有以前称为旧实施的地方都将被更新以调用您的代码的最新版本。
不像立即在屏幕上看到结果的结果那样简单。新代码需要实际调用。例如,如果您注入视图控制器,则需要强制重新播放。为了解决此问题,类可以实现@objc func injected()方法,该方法将在全类注入以执行显示的任何更新之后被调用。您可以使用的一种技术是在程序中的某个地方包括以下代码:
#if DEBUG
extension UIViewController {
@ objc func injected ( ) {
viewDidLoad ( )
}
}
#endif解决此问题的另一个解决方案是使用此博客文章介绍的Intext Swift软件包“托管”。
您无法注入更改对内存中数据列出的方式,即您无法使用存储添加,删除或重新排序属性。对于非最终类别,这也适用于添加或删除方法,因为用于调度的vtable本身就是一个数据结构,该数据结构一定不能改变注射。注射还无法弄清楚需要重新执行哪些代码以更新上面讨论的显示。另外,不要被访问控制所带走。 private属性和方法不能直接注入,尤其是在扩展中,因为它们不是global可插的符号。它们通常间接注射,因为它们只能在被注入的文件中添加,但这会引起混乱。最后,注射不能很好地应对注射过程中添加/重命名/删除的源文件。您可能需要构建和重新启动应用程序,甚至可以关闭并重新打开项目以清除旧的Xcode构建日志。
SwiftUi(如果有的话)比Uikit更适合注射,因为它具有更新显示屏的特定机制,但是您需要对要注入的每个View结构进行几个更改。最简单的方法是添加在注射时观察的属性:
@ ObserveInjection var forceRedraw此属性包装器可在Hotswiftui或Indext Swift软件包中找到。它本质上包含一个@Published Integer您的观点,观察到每次注射都会增加。您可以使用以下一项在整个项目中提供这些软件包之一:
@ _exported import HotSwiftUI
or
@ _exported import Inject您需要进行可靠的SwiftUI注入需要做出的第二个更改是通过使用.enableInjection()方法在这些软件包中扩展View将其包装在AnyView中“删除返回类型”。这是因为,当您添加或删除Swiftui元素时,它可以更改车身属性的混凝土返回类型,该属性构成可能崩溃的内存布局更改。总而言之,每个身体的尾端应始终看起来像这样:
var body : some View {
VStack or whatever {
// Your SwiftUI code...
}
. enableInjection ( )
}
@ ObserveInjection var redraw您可以将这些修改留在生产代码中,因为对于Release构建,它们将优化为No-Op。
Xcode 16中的新是SWIFT_ENABLE_OPAQUE_TYPE_ERASURE构建设置。默认情况下,此设置将打开,您无需明确擦除视图。您仍然需要@ObserveInjection才能迫使重新绘制。
有关更多信息,请参见Xcode 16.2发行说明。
这可以正常工作,但是您实际上需要运行IncectionIII.App的GitHub 4.8.0+版本之一,请设置一个用户默认值以选择加入并重新启动应用程序。
$ defaults write com.johnholdsworth.InjectionIII deviceUnlock any
然后,而不是加载注射捆绑包在“构建阶段”中运行此脚本:(您还需要关闭项目构建设置“用户脚本沙盒”)
RESOURCES=/Applications/InjectionIII.app/Contents/Resources
if [ -f "$RESOURCES/copy_bundle.sh" ]; then
"$RESOURCES/copy_bundle.sh"
fi
并且,在您的应用程序中,在启动时执行以下代码:
#if DEBUG
if let path = Bundle . main . path ( forResource :
" iOSInjection " , ofType : " bundle " ) ??
Bundle . main . path ( forResource :
" macOSInjection " , ofType : " bundle " ) {
Bundle ( path : path ) ! . load ( )
}
#endif切换到此配置后,使用模拟器时也可以工作。请咨询HotreloDading项目的回教徒,以获取有关如何调试程序将您的程序连接到wi-Fi的indectionii.app的详细信息。您还需要从“流行菜单”菜单中手动选择文件观察器的项目目录。
它有效,但您需要在开发过程中暂时关闭“硬化运行时”下的“应用沙盒”和“库验证”,以便可以动态加载代码。为了避免代码签名问题,请使用上述真实设备上注入的说明中详细介绍的新copy_bundle.sh脚本。
多年来,Indection一直使用各种方式,从Objective-C开始使用“ swizzling” API,但现在主要围绕着Apple链接器的功能“ Interposing”构建,该功能为任何类型的任何SWIFT方法或计算属性提供了解决方案。
当您的代码在Swift中调用一个函数时,通常“静态派遣”,即使用所调用的函数的“杂项符号”链接。但是,每当您将应用程序与“ - 可间接”选项链接起来时,都会添加附加级别的间接级别,以找到通过可写入的一部分调用的所有函数的地址。因此,使用操作系统的能力加载可执行代码和Fishhook库来“重新启用”呼叫,因此可以“插入”任何功能的新实现,并在运行时有效地将它们缝合到程序的其余部分中。从那时起,它将像已在程序中内置新代码一样执行。
Indection使用FSEventSteam API查看何时更改源文件,并扫描了最后的Xcode build Log,以进行重新编译并链接可以加载到程序中的动态库。然后,对注入的运行时支持加载动态库,并将其扫描为其包含的功能定义,然后将其“插入”到程序的其余部分中。这不是完整的故事,因为派遣非最终类方法使用了“ VTable”(想想C ++虚拟方法),该方法还必须进行更新,但是该项目将其与任何遗产Objective-C“ Swizzling”一起进行。
如果您有兴趣了解注射方式的工作原理,那么最佳来源是我的书Swift Secrets或IndectionLite Swift软件包中的新的,启动的参考实现。有关“插入”的更多信息,请咨询此博客文章或Fishhook项目的读书文件。有关应用程序本身组织的更多信息,请咨询Roadmap.md。
上班注入有三个组成部分。文件观察器,重新编译任何更改的文件并构建可以加载的动态库的代码,并在运行时将代码的新版本缝合到应用程序中。如何将这三个组件组合起来产生了可以使用注射的方式的数量。
“注入经典”是您从Github下载二进制版本之一并运行Indionctionii.App的地方。然后,您将该应用程序内部的一个捆绑包加载到您的程序中,如模拟器中所示。在此配置中,在应用程序内完成了文件观察器和源重编译,并且捆绑包使用套接字连接到应用程序,以了解何时准备加载新的动态库。
“ App Store Indection”此版本的应用程序是沙盒,当文件观察器仍在应用中运行时,重新编译和加载是在模拟器内部执行的。由于模拟器使用案例敏感文件系统,这可能会引起C头文件的问题,以作为对真实设备的忠实模拟。
“ HotRelodation Indection”是您在设备上运行应用程序的地方,因为您无法将Mac的文件系统从真实手机上加载,您将HotReloDaiding Swift软件包添加到项目(仅在开发过程中!),其中包含通常在捆绑包中执行动态负载的所有代码。这就要求您使用未包装的二进制版本之一。它也已被上述copy_bundle.sh脚本替换。
“独立注射”。这是该项目的最新演变,您不再运行应用程序本身,而只需加载一个注入束,并且文件观察器,重新兼容和注入都在模拟器中进行。默认情况下,该手表会更改主目录内的任何SWIFT文件,尽管您可以使用环境变量INJECTION_DIRECTORIES更改此手表。
注射液是独立注入的最小启动实施。只需添加此Swift软件包,您就应该能够注入模拟器。
IndectionNext是目前的注射剂,对于大型项目,应该更快,更可靠。它将其集成到XCode的调试标志中,以了解如何重新编译文件以避免解析构建日志并重新使用客户端从InjectionLite实现注入。要与外部编辑器(例如Cursor一起使用,IndectionNext还可以使用文件观察者来检测编辑并倒退以构建日志解析代码。
所有这些变化都要求您为调试构建添加“ -xlinker -interposble”链接标志,否则您只能注入非最终类方法,所有这些都可以与更高级别的注入或HotSwSwiftUi结合使用。
请咨询旧的读书文件,如果有任何内容简单地“信息过多”,包括可以用于自定义的各种环境变量。一些例子:
| 环境 | 目的 |
|---|---|
| indection_detail | 执行的所有动作的详细输出 |
| indection_trace | 注射功能的日志调用(v4.6.6+) |
| indection_host | MAC的IP地址用于开发器注入 |
使用Indection_Trace环境变量,注入任何文件都会将所有调用的记录添加到文件中的函数和方法以及其参数值以作为调试的帮助。
INDECTIONIII的一个鲜为人知的功能是,只要您在某个时候运行了应用程序的测试,您可以注入单个XCtest类,并且如果立即运行 - 报告每次修改它是否失败。
该项目包括Rentzsch/Mach_inject,Erwanb/Machinjectspample,Davedelong/Ddhotkey和ACJ/Timelapse-Builder-Swift的代码。
应用程序跟踪功能使用oliverletterer/imp_implementationforwardingtoselector蹦床通过MIT许可通过SwiftTrace项目实现。
SwiftTrace使用非常方便的https://github.com/facebook/fishhook。有关许可详细信息,请参见App Bundle中包含的项目源和标头文件。
该版本包括出色的Canviz库的一个非常略微修改的版本,以在HTML帆布中渲染“点”文件,该文件受到MIT许可证的约束。这些更改将通过节点的ID传递到节点标签标签(第212行),以逆转节点的渲染和链接它们的线(第406行),并存储边缘路径,以便可以在“ canviz-0.1/canviz.js”中涂色(66和303)。
它还包括CodeMirror JavaScript编辑器,以使用MIT许可下的注射来评估代码。
神话般的应用程序图标要归功于Pixel-Mixer.com的Katya。