发布时间:2025-12-09 15:57:43 浏览次数:12
iOS开发桥(idb)是一个多功能的工具,用于自动化iOS模拟器和设备。它在一个一致的、对人友好的界面中暴露了很多分布在苹果工具中的功能。
idb由两个部分组成,每个部分都需要单独安装。
每个目标(模拟器/设备)都会有一个附属进程,允许idb进行远程通信。
idb伴侣可以通过brew安装,也可以从源码构建
brew tap facebook/fbbrew install idb-companion注意:关于如何安装brew的说明可以在这里找到
提供了一个cli工具和python客户端来与idb交互。
它可以通过pip安装:
pip3.6 install fb-idb注意:idb客户端需要安装python 3.6或更高版本。
注意:关于如何安装pip的说明可以在这里找到
确保idb的两个部分都安装成功,然后你就可以开始了 请到快速入门处尝试一下idb。
这是一个快速入门指南,向你展示idb能做什么的一瞥。如果你还没有安装idb,请参考安装。
让我们从找出你的Mac上有哪些模拟器/设备开始。这将打印出你的Mac上的所有模拟器和所有连接的设备。
idb list-targets启动其中任何一个。
idb boot UDID试试下面的任何一个命令,并确保你通过–UDID来运行它们与正确的模拟器。
idb launch com.apple.Mapsidb recordidb log现在让我们试着运行测试。这将安装模拟器上提供的测试包。
idb xctest install Fixtures/Binaries/iOSUnitTestFixture.xctest为了验证它是否被正确安装,只要运行
idb xctest list通过发布这些命令来运行测试。
idb 是由两个具有不同职责的组件组成的。这两个组件都是idb运行命令的必要条件。
这是一个Python3 cli,暴露了idb所提供的所有功能。因为它是用Python写的,所以不需要从连接iPhone或iOS模拟器的Mac上运行。
cli本身是idb_companion客户端的一个薄包装。所有与idb_companion的通信都是通过gRPC完成的。这可以是通过TCP或Unix域套接字。
如果你愿意,这个客户端库可以被导入到你自己的python3代码中,或者可以从任何其他类型的自动化中调用CLI。
idb_companion是一个在MacOS上运行的Objective-C++的gRPC服务器。它与用于自动化仿真器和设备的本地API对话。它连接FBSimulatorControl和FBDeviceControl框架,这两个框架是整个idb项目的一部分。
当idb_companion作为gRPC服务器时,它是为一个单一的iOS目标(设备或模拟器)而做的。
此外,idb_companion还有一些故意不在python CLI中使用的命令,这些操作与iOS设备管理或模拟器生命周期的操作有关。
idb cli默认在两种模式中的一种运行:
iOS设备和模拟器的行为方式大不相同,模拟器的行为方式也与仿真器(例如Android模拟器)大不相同:
在 FBSimulatorControl 和 FBDeviceControl 中有两个框架,用来实现 idb 使用的大部分功能。此外,还有一个FBControlCore框架,它的存在是为了定义设备和模拟器框架的通用接口,并提供两者通用的其他功能。这些框架能够独立于idb本身使用。以下是这些框架是如何被设计在一起的概述
一个目标(FBiOSTarget)的实例是一个代表单个iOS模拟器或设备的对象。FBiOSTarget是一个协议定义,描述了由FBSimulator和FBDevice实现的功能。这种抽象意味着更高级别的应用程序和框架能够同样对待一个目标,无论它是一个iOS模拟器还是设备。
由于iOS模拟器和设备的操作方式有很大的不同,这种抽象级别允许框架在实现通用功能时平滑地处理这些差异。
一个 “目标集”(FBiOSTargetSet)表示一个目标的集合。这些在FBSimulatorSet和FBDeviceSet中都有实现。一个模拟器集代表一个根目录,该目录对许多模拟器来说是通用的。一个设备集代表所有连接到主机的设备。
这种抽象允许对模拟器和设备进行 "CRUD "操作的接口,尽管有不同的实现方式。例如,在模拟器和设备之间使用相同的API来擦除它们。
在整个框架中,有 "配置 "值,以Objective-C类实现。这些通常用于整合特定API调用所需的所有信息。例如,FBApplicationLaunchConfiguration定义了启动参数、环境和启动模式。
这些类型的存在是为了让API不需要极其冗长和繁琐的参数列表,以及提供合理的默认值。这些类型有意地尽可能地没有行为,接近于纯值类型。
为了在模拟器和设备之间保持一个共同的API,idb有一套Objective-C协议,为iOS模拟器和设备的单独实现定义了一个接口。这确实鼓励了精心设计的API的创建。idb_companion可以与底层的iOS目标无关,而是与这些协议进行交互。
可能有一些协议只被一个或另一个支持,这取决于目标。例如,在iOS模拟器上没有 "激活 "的概念,所以FBDeviceActivationCommands只由FBDevice实现。如果一个目标不支持给定的协议,当这些API被调用时,idb会失败,给出一个错误信息,解释缺少什么功能。
模拟器和设备之间的实现是完全分开的,并在它们各自的框架中实现。例如,FBApplicationCommands(它提供了一个在iOS目标上启动和列出应用程序的API),在FBSimulatorApplicationCommands和FBDeviceApplicationCommands中分别实现。
如果功能对模拟器和设备都是通用的,它的协议就会被添加到FBiOSTarget中,这样FBiOSTarget的实现者就需要实现它。对于不常见的功能,相关协议被添加到具体的FBSimulator或FBDevice类的定义中。对于非通用的协议,调用者必须在调用API之前检查协议的一致性,或者直接使用具体的类型。
FBControlCoreLogger被用于整个代码库。它提供了一个通用的接口,用于向系统级记录器以及文件进行日志记录。由于所有这些框架可能被用于各种不同的场景,包括日志客户端可能是远程的,这个抽象提供了一个通用的方式,将日志引导到适当的地方。这是一个 “非结构化的记录器”,它接收任意的字符串。
还有一些类用于拦截内部调用(FBLoggingWrapper),并将其记录到 “结构化日志器”(FBEventReporter)。这在idb中被用来对服务器中的所有API调用产生准确的日志记录。这支持用户定义的类,所以非常适合推送到支持聚合的数据存储中。
由于框架中所提供的功能的性质,IO是一项非常常见的任务。有许多抽象用于在各种源和汇之间读和写数据。例如,通用接口(FBFileReader)被用来从一个生成的应用程序进程中读取输出,并将其转发给消费者(FBFileWriter)。然后,这被用来通过idb的gRPC接口来管理应用程序的输出,而应用程序的启动器不需要知道是什么在消费输出。
所有这些都是由libdispatch支持的,由于它对异步IO的支持,文件的读写是以一种有效的方式管理的,而用户不必建立自己的IO复用器。
在框架内完成的大量 "工作 "都是基于IO和调用其他执行IO的API。这项工作是非常异步的,这意味着有充分的理由为执行和等待这项工作提供一个一致的API。
由于框架是Objective-C框架,以及Swift语言中没有像ync/await这样的API,FBFuture类被用来封装这种异步工作。这些功能最好存在于语言或标准库层面,以避免实现这类功能,不过这样做也有好处:
作为FBFuture的消费者的代码可以使用它的调用来接收完成后的回调,或者等待:它的结果同步地获得一个值。FBFuture的内部使用尽可能地避免了任何同步工作。idb的消费者不需要知道这些细节,因为它们是idb_companion的内部实现。
idb_companion的大部分工作是作为一个gRPC服务器来实现FBSimulatorControl和FBDeviceControl框架的所有功能。
它确实有一些组件对它的运行方式很重要。
这是idb_companion的入口,包括所有它支持的各种标志。它用于指定如何为一个给定的iOS目标启动gRPC服务器。
此外,它还暴露了一些 "CRUD "命令,这些命令对于管理模拟器和设备是有破坏性的。这些命令是故意不在gRPC接口中出现的,以防止不必要的行为。如果你想执行破坏性的命令,这些必须在iOS模拟器或设备所在的主机上执行。
这是一个C++类,实现了gRPC接口。这是一个C++类,因为idb_companion正在使用C++的gRPC库。
请求被转发到gRPC C++库内部的线程池上的处理程序。绝大多数对同伴的调用都是调用由FBFuture支持的API。这意味着处理程序线程将负责等待该FBFuture的解析。每个处理程序的调用都被包裹在一个自动释放池中,以防止内存泄漏。对底层框架的调用将适当地序列化工作,因为它们使用了Future。
这是一个纯粹的Objective-C类,它为底层框架中的许多API提供了一个门面,所以服务处理程序不需要知道它们是如何操作的。这也意味着C++只需要在必要的地方使用,所以如果gRPC处理程序被迁移到Swift,处理程序中的实现将是最小的。
在调用命令执行器之前,服务处理程序需要将C++类型转换为与之对应的Objective-C类型,因为请求和响应对象是通过C++ protobufs表达的。
idb cli是基于python的,可以简单地用pip来构建
pip3 install .这是一个本地的macOS可执行文件,通过Xcode构建。
首先,需要有系统级的构建依赖,这些可以通过homebrew安装:
# Tap for grpcbrew tap grpc/grpc# The grpc compiler is used to generate C++ bindings from the idb.proto definitionbrew install grpc# cocoapods is needed to resolve dependencies for the Xcode projectbrew install cocoapods# cocoapods is used to resolve the grpc runtime library for the companion# This must be run from the root of the idb repository to use the appropriate Podfilepod install这将打开一个Xcode项目,你可以构建和运行:
open idb_companion.xcworkspace打开Xcode项目后,你需要添加一个–UDID参数来启动。
一旦idb_companion启动,它将把同伴所绑定的TCP端口输出到stdout:
{"grpc_port":10882}。默认情况下,这个端口是10882,它可以用–端口0或你选择的端口来绑定一个随机端口。现在你可以通过传递给cli的IDB_COMPANION环境变量来指挥针对这个同伴的IDB命令:
$ IDB_COMPANION=localhost:10882 idb describe只要你在所有的命令前加上这个环境变量的前缀,你就可以在Xcode中对你目前正在调试的同伴运行命令。
所有idb cli命令都是针对一个特定的iOS目标(模拟器或设备)运行的。由于一个主机几乎肯定会有一个以上的目标连接,所以需要有一种方法来指定哪个iOS目标来运行命令。
所有iOS目标的共同点是存在一个UDID。iOS模拟器的UDID是基于NSUUID的,设备的UDID则取决于手机型号,但iPhone Wiki有一个格式概述。就idb而言,UDID只是一个字符串。
idb cli由idb_companion驱动,以执行所有的底层行为。由于idb_companion可以通过域套接字或TCP套接字暴露出来,所以也可以直接寻址一个同伴,而不是通过UDID寻址。
idb维护它所知道的iOS Target的本地状态。这个状态可以被修改或绕过。idb在很大程度上依靠idb_companion来处理请求。因此,所有操作iOS Targets的命令都需要一个同伴来服务它们。
如果你能在每个调用中传递同伴的位置,那么就不需要修改这个内部状态。所有的idb命令都可以以IDB_COMPANION环境变量为前缀,这将直接解决一个给定的同伴。如果提前知道同伴的位置(而不是只有一个UDID),这是寻址iOS目标的首选方式:
# Run a describe command against a companion running on the loopback interface# on TCP Port 10882$ IDB_COMPANION=localhost:10882 idb describe# This can also be a path to a domain socket that the companion is running on$ IDB_COMPANION=/tmp/idb_companion_domain_sock idb describe通过UDID而不是同伴地址来寻址是通过连接同伴来实现的,因此这种知识在调用中会持续存在:
# Connecting via a TCP companion serveridb connect COMPANION_HOST COMPANION_PORTSOME_UDID# Connecting via a unix domain socketidb connect COMPANION_DOMAIN_SOCKET_PATHSOME_UDID# SOME_UDID can then be used to address the connected target via --udididb还可以在后台透明地启动同伴,因此用户不需要知道如何启动同伴或直接寻址:
# This will implictly start a companion in the background and keep it alive$ idb connect TARGET_UDIDTARGET_UDID使用connect也是可选的;当针对给定的UDID执行命令时,如果没有为给定的UDID运行同伴,将以类似于idb connect的方式在后台启动一个同伴:
# When TARGET_UDID does not have a companion running for it, running describe will run it in the background.$ idb describe --udid TARGET_UDID与connect相反。这不会终止支持这个目标的同伴。
idb知道你手动连接的同伴,以及其他还没有同伴的iOS目标。这可以用list-targets来显示:
idb list-targets
输出将显示哪些目标有或没有同伴与之关联。
idb --boot UDID
这只适用于 iOS 模拟器。
返回关于指定目标的元数据,包括:
除了与特定命令相关的参数外,还有一些适用于所有命令的可选参数。
| –udid UDID | 目标的UDID | 如果只有一个目标被连接,它将使用那个目标。 |
| –log {DEBUG,INFO,WARNING,ERROR,CRITICAL} | 设置日志级别 | CRITICAL |
| –json | JSON结构化输出(如适用) | False |