FLEX
FLEX (Flipboard Explorer) 是一套针对iOS开发的内嵌调试和探索工具。当显示时,FLEX会在一个窗口中显示一个工具栏,您可以从这个工具栏中查看和修改运行中的应用程序中的几乎每一项状态。
赋予自己调试超级能力
- 检查和修改视图层次结构。
- 查看对象的属性和实例变量。
- 动态修改许多属性和实例变量。
- 动态调用实例和类方法。
- 观察包含时间、头信息和完整响应的详细网络请求历史。
- 添加自己的模拟器键盘快捷方式。
- 查看系统日志消息(例如来自
NSLog
)。 - 通过堆扫描访问任何活动对象。
- 在应用沙盒内查看文件系统。
- 在文件系统中浏览SQLite/Realm数据库。
- 使用控制、 Shift和命令键在模拟器中触发3D触摸。
- 探索应用及其链接系统框架中的所有类(公开和私有)。
- 快速访问如
[UIApplication sharedApplication]
、应用程序代理、关键窗口的根视图控制器等有用的对象。 - 动态查看和修改
NSUserDefaults
值。
与其他许多调试工具不同,FLEX完全在您的应用程序中运行,因此您不需要连接到LLDB/Xcode或不同的远程调试服务器。它对模拟器和物理设备都适用。
使用说明
在iOS模拟器中,您可以使用快捷键激活FLEX。按f
键切换FLEX工具栏。按?
键以查看完整快捷键列表。您还可以以编程方式显示FLEX
简短版本
// Objective-C
[[FLEXManager sharedManager] showExplorer];
// Swift
FLEXManager.shared.showExplorer()
完整版
#if DEBUG
#import "FLEXManager.h"
#endif
...
- (void)handleSixFingerQuadrupleTap:(UITapGestureRecognizer *)tapRecognizer
{
#if DEBUG
if (tapRecognizer.state == UIGestureRecognizerStateRecognized) {
// This could also live in a handler for a keyboard shortcut, debug menu item, etc.
[[FLEXManager sharedManager] showExplorer];
}
#endif
}
除外:tvOS
FLEX本身不支持tvOS。然而,其他人已经将FLEX移植到tvOS。如果您需要tvOS支持,可以查找这些分支之一。以下是其中一个分支:这里有一个分支
功能示例
修改视图
选择视图后,您可以通过点击工具栏下方的信息栏获取关于视图的更多信息。从这里,您可以修改属性并调用方法。
网络历史记录
开启网络调试后,您可以查看使用NSURLConnection或NSURLSession发出的所有请求。设置允许您调整缓存的响应体类型及响应缓存的最大容量。您可以在应用启动时自动开启网络调试。该设置在启动之间保持。
堆上的所有对象
FLEX查询为所有活动的分配内存块调用malloc,并搜索看起来像对象的那些。你可以从这里看到一切。
在地址上探索
如果你得到一个任意地址,你可以尝试探索该地址的对象,如果FLEX能验证地址指向一个有效的对象,它将打开它。如果FLEX不确定,它将警告你并拒绝取消引用指针。然而,如果你知道更好的,你可以选择通过选择“不安全探索”来探索它。
模拟器键盘快捷键
默认键盘快捷键允许您激活FLEX工具,使用箭头键进行滚动,并使用escape键关闭模态框。您还可以通过-[FLEXManager registerSimulatorShortcutWithKey:modifiers:action:description]
添加自定义键盘快捷键。
文件浏览器
在你的应用程序的包或沙盒容器内查看文件系统。FLEX显示文件大小、图像预览以及格式化显示.json
和.plist
文件。你可以重命名和删除文件或文件夹。如果你想在外部检查文件,你可以“共享”任意文件。
SQLite浏览器
SQLite数据库文件(文件扩展名为.db
或.sqlite
),或Realm数据库文件,可以使用FLEX进行浏览。数据库浏览器允许您查看所有表,通过点击列标题可以对单个表进行排序。
模拟器中的3D Touch
使用命令、控制和Shift键的组合,您可以在模拟器中模拟不同级别的3D Touch压力。每个键贡献最大可能力量的1/3。请注意,您需要稍微移动触摸以获取压力更新。
探索加载的库
深入挖掘公共和私有内容。要了解更多关于类的信息,您可以创建其实例并探索其默认状态。您也可以输入类名直接跳转到该类,如果您知道要查找的是哪个类。
NSUserDefaults编辑
FLEX允许您编辑任何组合的字符串、数字、数组和字典的默认值。输入被解析为JSON
。如果为默认键设置了其他类型的对象(例如,NSDate
),则可以查看它们但不能编辑。
从其他应用程序中学习
代码注入留给读者练习。
安装
FLEX需要以iOS 9或更高版本为目标的应用。要运行示例项目,在示例文件夹中打开终端窗口,并运行pod install
,然后打开生成的工作区。
CocoaPods
FLEX可用于CocoaPods。只需将以下行添加到您的podfile中
pod 'FLEX', :configurations => ['Debug']
Carthage
在您的Cartfile中添加以下内容
github "flipboard/FLEX"
Buck
如果您使用Buck,您可能希望沉默一些FLEX发出的警告。您需要将FLEX作为apple_library
构建,并传递-Wno-unsupported-availability-guard
标志,以及其他以下警告标志以禁用FLEX可能具有的任何其他警告。
手动方式
手动将Classes/
中的文件添加到您的Xcode项目,或直接拖动整个FLEX/
文件夹。请确保在Release
构建中排除FLEX,否则您的应用将不会被接受。
禁言警告
将以下标志添加到Build Settings:
中的“Other Warnings Flags”:
-Wno-deprecated-declarations
-Wno-strict-prototypes
-Wno-unsupported-availability-guard
Swift Package Manager
将依赖项包含在Package.swift
的dependencies
值中
dependencies: [
.package(url: "https://github.com/FLEXTool/FLEX.git", .upToNextMajor(from: "4.3.0"))
]
接下来,将库包含在您的目标中
.target(
name: "YourDependency",
dependencies: [
"FLEX"
]
)
从发布(App Store)构建中排除FLEX
FLEX使得探索应用程序内部变得简单易行,因此它不是应该向用户公开的东西。幸运的是,可以从发布构建中轻松排除FLEX文件。策略取决于您在项目中如何集成FLEX,以下进行了描述。
用#if DEBUG
语句包裹与FLEX集成的代码之处,以确保工具仅在您的调试
构建中可用,并避免在发布
构建中出错。有关集成FLEX的更多帮助,请参阅示例项目。
CocoaPods
CocoaPods如果只指定了FLEX的Debug配置,则会自动将其从发布构建中排除。
pod 'FLEX', :configurations => ['Debug']
Carthage
-
不要将
FLEX.framework
添加到目标中嵌入的二进制文件,否则它将被包含在所有构建中(因此也包括发布构建)。 -
相反,将
$(PROJECT_DIR)/Carthage/Build/iOS
添加到您的目标框架搜索路径中(如果已经通过Carthage包括其他框架,则此设置可能已经存在)。这使其能够从源文件导入FLEX框架。即使为所有配置添加此设置也没有坏处,但至少应将其添加到调试配置中。 -
向您的目标添加一个运行脚本阶段(例如,在现有的
Link Binary with Libraries
阶段之后插入),该阶段只会将FLEX.framework
嵌入到调试构建中。if [ "$CONFIGURATION" == "Debug" ]; then /usr/local/bin/carthage copy-frameworks fi
最后,将
$(SRCROOT)/Carthage/Build/iOS/FLEX.framework
作为此脚本阶段输入文件的输入。
Swift Package Manager
在Xcode中,转到构建设置 > 构建选项 > 排除源文件名
。对于您的发布
配置,将其设置为FLEX*
,如下所示,以排除所有以FLEX
为前缀的文件。
手动添加到项目中的 FLEX 文件
在Xcode中,转到构建设置 > 构建选项 > 排除源文件名
。对于您的发布
配置,将其设置为FLEX*
,如下所示,以排除所有以FLEX
为前缀的文件。
附加说明
- 当设置类型为
id
或NSUserDefaults
中的值时,FLEX 尝试将输入字符串解析为JSON
。这允许您使用字符串、数字、数组和字典的组合。如果您想设置字符串值,它必须用引号括起来。对于显式类型化为NSString
的 ivars 或属性,不需要引号。 - 在使用 FLEX 时,您可能想禁用异常断点。FLEX 使用的一些函数在接收到无法处理的输入时将抛出异常(例如,
NSGetSizeAndAlignment()
)。FLEX 捕获这些异常以避免崩溃,但如果断点处于活动状态,您的断点将被触发。
感谢 & 致谢
FLEX 建立在先前的开源工具灵感和想法之上。以下资源特别有帮助
- MirrorKit:Objective-C 运行时的 Objective-C 包装器。
- DCIntrospect:iOS 模拟器的视图层次结构调试。
- PonyDebugger:使用 Chrome 开发者工具界面进行网络、核心数据和视图层次结构调试。
- Mike Ash:编写良好、信息丰富的关于所有 obj-c 事情的博客文章。以下链接对本次项目特别有用
- MAObjCRuntime
- 让我们构建键值编码
- ARM64 和您
- RHObjectiveBeagle:一个用于扫描堆中活动对象的工具。需要注意的是,由于许可问题,没有查看 RHObjectiveBeagle 的源代码。
- heap_find.cpp:枚举 malloc 块以在堆中查找对象的示例。
- Gist from @samdmarshall:另一个枚举 malloc 块的示例。
- Non-pointer isa:关于 iOS 上 isa 字段变化以及提及有用的
objc_debug_isa_class_mask
变量的解释。 - GZIP:用于在 iOS 上使用 libz 压缩/解压缩数据的库。
- FMDB:这是一个围绕 SQLite 构建的 Objective-C 封装。
- InAppViewDebugger:由 @indragiek 启发并实现的 FLEX 4 的 3D 视图探索器的参考实现。
贡献
请参阅我们的贡献指南。
待办事项
- Swift 运行时内省(Swift 类、堆上的 Swift 对象等)
- 动态添加新的 NSUserDefault 键/值对