在本章中,我们着眼于 Apple Silicon Mac 上的崩溃,比如,因使用 Rosetta 翻译系统而引起的崩溃以及因在 macOS 上运行的未修改 iOS 应用程序而引起的崩溃。 此外,我们还将研究同时支持 ARM 和 Intel CPU的多体系结构代码可能导致的新型崩溃。
什么是 Apple Silicon Mac?
Apple Silicon表示该芯片的设计来自Apple,而不是第三方。 苹果公司的A系列芯片可以被认为是苹果芯片。 但是,本章重点是Apple Silicon Macs。 这些始于Apple M1芯片。 这些Mac之所以不被称为 基于ARM的Mac,可能是因为Apple在设计水平上做出了重大贡献,同时仍然符合ARM ABI。 当从基于 Intel 的 Mac 切换到 Apple Silicon Mac 时,这为客户带来了更优益的市场收益,例如更长的电池寿命和高性能。
什么是 Rosetta?
Rosetta 是 Apple Silicon Mac 上的指令翻译器。当应用程序将 Intel 指令作为二进制代码的一部分时,它可以将这些指令转换为 ARM 指令,然后运行它们。可以把它看作 AOT 编译器。这项技术的起源可以追溯到更早的时期,当时 mac 正在从 PowerPC 芯片过渡到 Intel 芯片。苹果在 Transitive Technologies Ltd. 的技术帮助下研发出了 Rosetta 的第一个版本。在 Rosetta 的第二个版本中,我们的系统允许在每个进程的基础上,将 Intel 指令预先翻译成 ARM 指令,然后以原生速度运行。
Rosetta 的二进制文件
在 Apple Silicon Mac 上,Rosetta 软件驻留在
/Library/Apple/usr/libexec/oah
在这个目录下有运行时引擎 runtime_t8027
、翻译器 oahd-helper
、命令行工具 translate_tool
和其他工具。它的操作对终端用户来说基本是透明的,除了启动延迟较小或性能稍低。从崩溃分析的角度来看,我们可以从内存占用量,异常帮助程序和运行时帮助程序的角度看到它的存在。
Rosetta 的局限性
Rosetta 是一个功能强大的系统,但有一些局限性。这些主要涉及高性能多媒体应用程序和操作系统虚拟化解决方案。
Rosetta 并不包括以下功能:
- 内核扩展
-
x86_64
虚拟化支持说明 - 矢量指令,例如 AVX,AVX2 和 AVX512
有趣的是,Rosetta 支持即时编译应用程序。这些应用程序非常特殊,因为它们自己生成代码,然后执行代码。大多数应用程序都只有固定的只读代码(程序文本),然后执行这些代码,它们的数据只是可变的(但不是可执行的)。这大概是因为JIT是JavaScript运行时的常用技术。
Apple 建议在调用使用这种功能的代码之前先检查可选的硬件功能。 我们可以通过运行 sysctl hw | grep optional
来确定平台上存在哪些可选硬件支持。在代码中,我们可以调用sysctlbyname
方法来实现同样的功能。
强制执行 Rosetta
如果我们默认给自己的项目使用标准的构建选项,当在 Debug
时将Build Active Architecture Only
设置成为Yes
,而对于Release
构建则设置为No
,然后再调试时,我们将只看到本机的二进制文件。 这是因为在 Debug
时,我们不想浪费时间来构建与我们正在测试的机器无关的体系结构。
如果我们进行 Archive
构建,Product > Archive
,然后选择 Distribute App
我们最终获得了一个可供发布的版本。 在默认设置下,这将是 Fat Binary(我们将其称为胖二进制)文件,在多体系结构的二进制文件中提供 x86
和 arm64
。
一旦我们有了一个 Fat Binary 文件,我们可以使用 Finder
应用程序,右键单击File info
设置 Rosetta 来执行我们的二进制文件的翻译,这样在一个 Apple Silicon Mac 上,Intel 指令就会从 Fat Binary 中翻译出来。
翻译后的应用程序示例
本章的工作示例是icdab_thread
程序。 可以在网上找到。@icdabgithub 该程序尝试调用 thread_set_state
,然后在 60 秒后调用abort
主动崩溃。实际上它并没有办法达到这个效果,因为最近 macOS 的安全增强,以防止使用这样的API,它是恶意软件的攻击载体。尽管如此,这个程序还是很有趣的,因为在崩溃时,一个紧密相关的部分task_for_pid
被多次调用了 。
我们已经将命令行可执行程序 icdab_thread
修改为仅调用相同基础代码的应用程序。这个应用程序就是 icdab_rosetta_thread
。这是因为 UNIX 命令行可执行文件不适合运行转换后的程序,而应用程序可以。
icdab_rosetta_thread
Lipo 信息
以下命令显示我们的应用程序同时支持 ARM 和 Intel 指令。
# lipo -archs
icdab_rosetta_thread.app/Contents/MacOS/icdab_rosetta_thread
x86_64 arm64
翻译后的程序崩溃
如果我们运行 icdab_rosetta_thread
应用程序,点击 Start Threads Test
,在一分钟后,应用程序发生崩溃。比较原生案例与已翻译案例之间的崩溃分析,我们可以从崩溃报告中的找到差异。
代码类型
Code Type: ARM-64 (Native)
当在本地运行时,变成了已翻译
Code Type: X86-64 (Translated)
线程转储
崩溃的线程(和其他线程)看起来很相似,只是指针在翻译后的情况下基于更高的指针。
对于原生的崩溃,我们有:
Thread 1 Crashed:: Dispatch queue: com.apple.root.default-qos
0 libsystem_kernel.dylib 0x00000001de3015d8
__pthread_kill + 8
1 libsystem_pthread.dylib 0x00000001de3accbc
pthread_kill + 292
2 libsystem_c.dylib 0x00000001de274904 abort
+ 104
3 perivalebluebell.com.icdab-rosetta-thread
0x00000001002cd478 start_threads + 244
4 perivalebluebell.com.icdab-rosetta-thread
0x00000001002cd858 thunk for @escaping @callee_guaranteed () ->
() + 20
5 libdispatch.dylib 0x00000001de139658
_dispatch_call_block_and_release + 32
6 libdispatch.dylib 0x00000001de13b150
_dispatch_client_callout + 20
7 libdispatch.dylib 0x00000001de13e090
_dispatch_queue_override_invoke + 692
8 libdispatch.dylib 0x00000001de14b774
_dispatch_root_queue_drain + 356
9 libdispatch.dylib 0x00000001de14bf6c
_dispatch_worker_thread2 + 116
10 libsystem_pthread.dylib 0x00000001de3a9110
_pthread_wqthread + 216
11 libsystem_pthread.dylib 0x00000001de3a7e80
start_wqthread + 8
而对于翻译后的案例中崩溃,则有
Thread 1 Crashed:: Dispatch queue: com.apple.root.default-qos
0 ??? 0x00007fff0144ff40 ???
1 libsystem_kernel.dylib 0x00007fff6bdc4812
__pthread_kill + 10
2 libsystem_c.dylib 0x00007fff6bd377f0 abort
+ 120
3 perivalebluebell.com.icdab-rosetta-thread
0x0000000100d1c5ab start_threads + 259
4 perivalebluebell.com.icdab-rosetta-thread
0x0000000100d1ca1e thunk for @escaping @callee_guaranteed () ->
() + 14
5 libdispatch.dylib 0x00007fff6bbf753d
_dispatch_call_block_and_release + 12
6 libdispatch.dylib 0x00007fff6bbf8727
_dispatch_client_callout + 8
7 libdispatch.dylib 0x00007fff6bbfad7c
_dispatch_queue_override_invoke + 777
8 libdispatch.dylib 0x00007fff6bc077a5
_dispatch_root_queue_drain + 326
9 libdispatch.dylib 0x00007fff6bc07f06
_dispatch_worker_thread2 + 92
10 libsystem_pthread.dylib 0x00007fff6be8c4ac
_pthread_wqthread + 244
11 libsystem_pthread.dylib 0x00007fff6be8b4c3
start_wqthread + 15
注意,在翻译后的案例中,线程堆栈 0 中的实际代码行是 ???
。 大概这是 Rosetta 合成的实际翻译代码。
此外,在翻译后的案例中,我们还有另外两个线程,异常服务器和运行时环境:
Thread 3:: com.apple.rosetta.exceptionserver
0 runtime_t8027 0x00007ffdfff76af8
0x7ffdfff74000 + 11000
1 runtime_t8027 0x00007ffdfff803cc
0x7ffdfff74000 + 50124
2 runtime_t8027 0x00007ffdfff82738
0x7ffdfff74000 + 59192
Thread 4:
0 runtime_t8027 0x00007ffdfffce8ac
0x7ffdfff74000 + 370860
崩溃的线程状态寄存器
在原生的例子中,我们得到了线程状态寄存器:
Thread 1 crashed with ARM Thread State (64-bit):
x0: 0x0000000000000000 x1: 0x0000000000000000 x2:
0x0000000000000000 x3: 0x0000000000000000
x4: 0x000000000000003c x5: 0x0000000000000000 x6:
0x0000000000000000 x7: 0x0000000000000000
x8: 0x00000000000005b9 x9: 0xb91ed5337c66d7ee x10:
0x0000000000003ffe x11: 0x0000000206c1fa22
x12: 0x0000000206c1fa22 x13: 0x000000000000001e x14:
0x0000000000000881 x15: 0x000000008000001f
x16: 0x0000000000000148 x17: 0x0000000200e28528 x18:
0x0000000000000000 x19: 0x0000000000000006
x20: 0x000000016fbbb000 x21: 0x0000000000001707 x22:
0x000000016fbbb0e0 x23: 0x0000000000000114
x24: 0x000000016fbbb0e0 x25: 0x000000020252d184 x26:
0x00000000000005ff x27: 0x000000020252d6c0
x28: 0x0000000002ffffff fp: 0x000000016fbbab70 lr:
0x00000001de3accbc
sp: 0x000000016fbbab50 pc: 0x00000001de3015d8 cpsr:
0x40000000
far: 0x0000000100ff8000 esr: 0x56000080
在翻译后的案例中,同样也有线程状态寄存器:
Thread 1 crashed with X86 Thread State (64-bit):
rax: 0x0000000000000000 rbx: 0x000000030600b000 rcx:
0x0000000000000000 rdx: 0x0000000000000000
rdi: 0x0000000000000000 rsi: 0x0000000000000003 rbp:
0x0000000000000000 rsp: 0x000000000000003c
r8: 0x000000030600ad40 r9: 0x0000000000000000 r10:
0x000000030600b000 r11: 0x00007fff6bd37778
r12: 0x0000000000003d03 r13: 0x0000000000000000 r14:
0x0000000000000006 r15: 0x0000000000000016
rip: rfl: 0x0000000000000287
翻译的代码信息
在翻译后的案例中,我们会获得更多信息,这可能对那些从事调试 Rosetta 的工程师有用:
Translated Code Information:
tmp0: 0xffffffffffffffff tmp1: 0x00007fff0144ff14 tmp2:
0x00007fff6bdc4808
外部修改摘要
在原生的例子中,我们看到:
External Modification Summary:
Calls made by other processes targeting this process:
task_for_pid: 0
thread_create: 0
thread_set_state: 0
Calls made by this process:
task_for_pid: 0
thread_create: 0
thread_set_state: 0
Calls made by all processes on this machine:
task_for_pid: 914636
thread_create: 0
thread_set_state: 804
我们的代码曾尝试调用 thread_set_state
,但未能(由于 macOS 限制,在任何平台配置下都不行)。
然后我们看一下翻译后的示例:
External Modification Summary:
Calls made by other processes targeting this process:
task_for_pid: 1
thread_create: 0
thread_set_state: 0
Calls made by this process:
task_for_pid: 0
thread_create: 0
thread_set_state: 0
Calls made by all processes on this machine:
task_for_pid: 915091
thread_create: 0
thread_set_state: 804
我们看到几乎相同的统计信息,但是有趣的是,我们将task_for_pid
设置为 1。因此,翻译环境仅对翻译过程进行了最小的观察/修改。
虚拟内存区域
该程序的翻译版本在 RAM 使用率上比原生版本高。
在原生的案例中,我们可以看到:
VIRTUAL REGION
REGION TYPE SIZE COUNT (non-coalesced)
=========== ======= =======
TOTAL 1.7G 2053
TOTAL, minus reserved VM space 1.3G 2053
而翻译后的情况则是:
REGION TYPE SIZE COUNT (non-coalesced)
=========== ======= =======
TOTAL 5.4G 1512
TOTAL, minus reserved VM space 5.1G 1512
请注意,在翻译后的情况下,我们为 Rosetta 提供了其他虚拟内存区域:
Rosetta Arena 2048K 1
Rosetta Generic 864K 19
Rosetta IndirectBranch 512K 1
Rosetta JIT 128.0M 1
Rosetta Return Stack 192K 12
Rosetta Thread Context 192K 12
Rosetta 崩溃
Rosetta 是功能强大的翻译系统。 但是它不能翻译所有 X86-64指令。 例如,矢量指令无法翻译,遇到时会发生崩溃。
在诊断特定问题之前,有必要先熟悉一下 Apple 的 Porting Guide,因为这可以帮助我们针对程序可能崩溃的原因提出合理的假设。
icdab_avx
矢量指令崩溃
当在使用翻译运行应用程序的 Apple Silicon Mac 上遇到英特尔 AVX 矢量指令\index{Vector instruction!AVX} 时,我们就会崩溃。我们用一个示例应用程序 icdab_avx
来演示了这一点。
崩溃代码类型将为:
Code Type: X86-64 (Translated)
崩溃类型将为EXC_BAD_INSTRUCTION
,如下所示:\index{signal!SIGILL}
Exception Type: EXC_BAD_INSTRUCTION (SIGILL)
Exception Codes: 0x0000000000000001, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Termination Signal: Illegal instruction: 4
Termination Reason: Namespace SIGNAL, Code 0x4
Terminating Process: exc handler [26823]
在我们的情况下,崩溃时的线程状态为:
Thread 0 crashed with X86 Thread State (64-bit):
rax: 0x0000000000000001 rbx: 0x0000600001fcf5c0 rcx:
0x00007f87d143f8c0 rdx: 0x00007f87d143f8c0
rdi: 0x00000001047d6fa0 rsi: 0x00000001047d770a rbp:
0x000000030d132ab0 rsp: 0x000000030d132ab0
r8: 0x0000000000000003 r9: 0x0000000104b0e000 r10:
0x00000001047dc702 r11: 0x00000001047d57d0
r12: 0x00006000012d5100 r13: 0x00007fff6a9d4000 r14:
0x00007f87d143f8c0 r15: 0x00000001047d770a
rip: 0x00000001047d56cb rfl: 0x0000000000000206
应用程序二进制文件(程序文本)按如下方式加载:
Binary Images:
0x1047d4000 - 0x1047d7fff
+perivalebluebell.com.icdab-avx (1.0 - 1)
<3D9E0DED-2C66-30EE-AC6C-7C426246332E>
/Users/USER/Desktop/*/icdab_avx.app/Contents/MacOS/icdab_avx
如果我们发现Apple Silicon Mac 以这种方式使我们的应用程序崩溃,如果我们有这样的怀疑,我们可以迅速搜索任何矢量指令 \index{Vector instruction!AVX}。
# objdump -d icdab_avx.app/Contents/MacOS/icdab_avx | grep vmov |
head
100004527: c5 fa 10 84 24 a4 00 00 00 vmovss 164(%rsp),
%xmm0
100004530: c5 fa 10 8c 24 a0 00 00 00 vmovss 160(%rsp),
%xmm1
10000453f: c5 fa 10 8c 24 a8 00 00 00 vmovss 168(%rsp),
%xmm1
10000454e: c5 fa 10 8c 24 ac 00 00 00 vmovss 172(%rsp),
%xmm1
10000455d: c5 fa 10 8c 24 b4 00 00 00 vmovss 180(%rsp),
%xmm1
100004566: c5 fa 10 94 24 b0 00 00 00 vmovss 176(%rsp),
%xmm2
100004575: c5 fa 10 94 24 b8 00 00 00 vmovss 184(%rsp),
%xmm2
100004584: c5 fa 10 94 24 bc 00 00 00 vmovss 188(%rsp),
%xmm2
100004593: c5 f8 29 8c 24 90 00 00 00 vmovaps %xmm1,
144(%rsp)
10000459c: c5 f8 29 84 24 80 00 00 00 vmovaps %xmm0,
128(%rsp)
但是,更确切地说,我们可以在崩溃时使用指令指针。
我们从崩溃的线程状态中看到,我们有:
rip: 0x00000001047d56cb rfl: 0x0000000000000206
我们从Binary Images中看到,该程序已加载到地址0x1047d4000
。
使用我们在 Symbolification 章节中探讨的技术,我们可以在 Hopper 中加载 icdab_avx 二进制文件,将二进制文件的基址更改为0x1047d4000
,然后转到指令指针 rip
和地址 0x00000001047d56cb
。
然后,我们看到程序集转储:
_compute_delta:
push rbp ; CODE
XREF=_$s9icdab_avx14ViewControllerC31runVectorOperationsButtonAc
tionyySo12NSButtonCellCF+32
mov rbp, rsp
and rsp, 0xffffffffffffffe0
sub rsp, 0x160
mov dword [rsp+0x160+var_A4], 0x40000000
mov dword [rsp+0x160+var_A8], 0x40800000
mov dword [rsp+0x160+var_AC], 0x40c00000
mov dword [rsp+0x160+var_B0], 0x41000000
mov dword [rsp+0x160+var_B4], 0x41200000
mov dword [rsp+0x160+var_B8], 0x41400000
mov dword [rsp+0x160+var_BC], 0x41600000
mov dword [rsp+0x160+var_C0], 0x41800000
vmovss xmm0, dword [rsp+0x160+var_BC]
vmovss xmm1, dword [rsp+0x160+var_C0]
因此,虽然我们没有找到失败的确切指令,但是找到了出现错误的函数 compute_delta
,它位于runVectorOperationsButtonAction
方法中,它看起来已经被内联到这个版本二进制文件中。尽管如此,我们已经获得了足够的帮助,能够在相关区域中探索二进制文件并确定确认进行了向量操作vmovss
。 Rosetta不支持此功能。
导致该问题的原始代码为:
void
compute_delta() {
/* Initialize the two argument vectors */
__m256 evens = _mm256_set_ps(2.0, 4.0, 6.0, 8.0, 10.0, 12.0,
14.0, 16.0);
__m256 odds = _mm256_set_ps(1.0, 3.0, 5.0, 7.0, 9.0, 11.0,
13.0, 15.0);
/* Compute the difference between the two vectors */
__m256 result = _mm256_sub_ps(evens, odds);
/* Display the elements of the result vector */
float* f = (float*)&result;
printf("%f %f %f %f %f %f %f %f\n",
f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7]);
return;
}
为了避免此问题,我们应该使用一个有用的方法来检测当前环境是否支持 AVX\index{Vector instruction!AVX},如下所示:
bool
avx_v1_supported() {
int ret = 0;
size_t size = sizeof(ret);
if (sysctlbyname("hw.optional.avx1_0", &ret, &size, NULL, 0)
== -1)
{
if (errno == ENOENT)
return false;
return false;
}
bool supported = (ret != 0);
return supported;
}
如果支持 AVX 版本1(并且在检索信息时没有错误),此函数返回 true
。
在 Mac 上运行 iOS
由于 Apple Silicon Mac 和 iOS 设备共享相同的 ARM CPU 体系结构,因此Apple提供了附加功能。 未经修改的 iOS 应用程序可能会在基于 ARM 的 macOS 上运行。 为此,基于 ARM 的 macOS 拥有了一些特殊的 iOS 支持库。
我们可以这样想,macOS (主语)是提供支持库和框架的 ,提供 iOS( 宾语 )应用程序期望的 UIKit。
当此类应用程序崩溃时,我们会获得崩溃报告,该报告是 macOS 崩溃报告,但大多数细节都涉及 iOS 库。
icdab_wrap
iOS 应用程序在 macOS 上崩溃
如果我们在 Apple Silicon Mac 上运行 iOS 应用 icdab_wrap
,它可以正常加载,是因为 macOS 提供了 UIKit
框架,而 icdab_wrap
也假定该框架已存在。该应用程序是为了来演示一个问题,解包 Nil 可选项。
崩溃时,我们可以看到:
Code Type: ARM-64 (Native)
Parent Process: ??? [1]
Responsible: icdab_wrap [2802]
User ID: 501
这表明应用程序正在运行原生代码,而不是翻译过的代码。
Date/Time: 2020-11-14 11:58:17.668 +0000
OS Version: Mac OS X 10.16 (20A5343i)
Report Version: 12
Anonymous UUID: 0118DF8D-2876-0263-8668-41B1482DDC38
这表明我们很明显的是在 Mac 上运行。
System Integrity Protection: enabled
Crashed Thread: 0 Dispatch queue: com.apple.main-thread
Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: EXC_ARM_BREAKPOINT at 0x00000001c6c8f1f0
(brk 1)
Exception Note: EXC_CORPSE_NOTIFY
Termination Signal: Trace/BPT trap: 5
Termination Reason: Namespace SIGNAL, Code 0x5
Terminating Process: exc handler [2802]
Application Specific Information:
dyld3 mode
Fatal error: Unexpectedly found nil while implicitly unwrapping
an Optional value: file icdab_wrap/PlanetViewController.swift,
line 45
这说明程序崩溃时正在解包 Nil 可选项。
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libswiftCore.dylib 0x00000001c6c8f1f0
closure #1 in closure #1 in closure #1 in
_assertionFailure(_:_:file:line:flags:) + 404
1 libswiftCore.dylib 0x00000001c6c8f1f0
closure #1 in closure #1 in closure #1 in
_assertionFailure(_:_:file:line:flags:) + 404
2 libswiftCore.dylib 0x00000001c6c8e660
_assertionFailure(_:_:file:line:flags:) + 488
3 www.perivalebluebell.icdab-wrap 0x0000000104937da4
PlanetViewController.imageDownloaded(_:) + 196
(PlanetViewController.swift:45)
.
.
.
16 com.apple.AppKit 0x0000000189740824
_DPSNextEvent + 880
17 com.apple.AppKit 0x000000018973f1fc
-[NSApplication(NSEvent)
_nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 1300
18 com.apple.AppKit 0x00000001897313c4
-[NSApplication run] + 600
19 com.apple.AppKit 0x0000000189703550
NSApplicationMain + 1064
20 com.apple.AppKit 0x00000001899e92f8
_NSApplicationMainWithInfoDictionary + 24
21 com.apple.UIKitMacHelper 0x00000001bd9a6038
UINSApplicationMain + 476
22 com.apple.UIKitCore 0x00000001d333d1b0
UIApplicationMain + 2108
23 www.perivalebluebell.icdab-wrap 0x0000000104933a38 main +
88 (AppDelegate.swift:12)
24 libdyld.dylib 0x00000001c758ca50 start
+ 4
这表明当程序被加载时 libdyld.dylib
,它所期望的 UIKit UIKitCore
是通过 UIKitMacHelper
支持层实现的。
Binary Images:
0x10492c000 - 0x10493bfff
+www.perivalebluebell.icdab-wrap (1.0 - 1)
/Users/USER/Library/Developer/Xcode/DerivedData/icdab-gbtgrhpqeh
gqogaglrpuvzajteku/Build/Products/Debug-iphoneos/icdab_wrap.app/i
cdab_wrap
0x104a1c000 - 0x104a27fff
libobjc-trampolines.dylib (817)
<4A2C66DE-9358-3AE9-A69F-36687DB19CE3>
/usr/lib/libobjc-trampolines.dylib
0x104b34000 - 0x104baffff dyld (828)
<7A9F335B-50E3-3018-A9CC-26E57B61D907> /usr/lib/dyld
.
.
0x189700000 - 0x18a400fff com.apple.AppKit (6.9 -
2004.102) <96941AAC-01D7-36E7-9253-2C1187864719>
/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit
.
.
0x1bd77f000 - 0x1bd9a1fff com.apple.UIFoundation
(1.0 - 714)
/System/Library/PrivateFrameworks/UIFoundation.framework/Version
s/A/UIFoundation
0x1bd9a2000 - 0x1bda37fff com.apple.UIKitMacHelper
(1.0 - 3979.1.400)
/System/Library/PrivateFrameworks/UIKitMacHelper.framework/Versi
ons/A/UIKitMacHelper
0x1bda38000 - 0x1bda4bfff com.apple.UIKitServices
(1.0 - 1)
/System/Library/PrivateFrameworks/UIKitServices.framework/Versio
ns/A/UIKitServices
.
.
0x1c9b1b000 - 0x1c9b1bfff
com.apple.MobileCoreServices (1112.0.10 - 1112.0.10)
<992DAEC7-6964-3686-A910-4365B353D925>
/System/iOSSupport/System/Library/Frameworks/MobileCoreServices.
framework/Versions/A/MobileCoreServices
.
.
0x1d333a000 - 0x1d462ffff com.apple.UIKitCore (1.0
- 3979.1.400) <023078DD-44DA-3A11-82CA-12F8412661A2>
/System/iOSSupport/System/Library/PrivateFrameworks/UIKitCore.fr
amework/Versions/A/UIKitCore
.
.
0x1d72fa000 - 0x1d7337fff libswiftUIKit.dylib (15)
<68377BCA-6493-3E34-920E-0765BD07F2A7>
/System/iOSSupport/usr/lib/swift/libswiftUIKit.dylib
在大多数情况下,这些崩溃可以像在 iOS 上崩溃一样直接进行分析。问题很有可能是由于不同的物理环境导致的。例如,iOS 设备具有陀螺仪,而 macOS 设备则没有。
在 Mac 上支持 iOS 应用程序
Apple 提供了在 Mac 上部署 iOS 应用程序的最佳实践指南。你可以进行如下操作:
- 如果确定自己的 iOS 应用程序不适合 macOS。在 App Store Connect 中可以取消配置。
- 允许在 iOS 上安装的应用程序安装在 macOS 上,但是添加检查可用的可选硬件功能。
- 增强该应用程序,使 Mac 用户更易于使用替代功能。 例如,添加iOS键盘支持。
- 添加代码以检测 iOS-on-Mac 的场景。
- 通过 Mac Catalyst 技术连接到Mac。这意味着首先得拥有一个出色的iPadOS应用。
- 编写纯原生 macOS 应用程序。
例如,在应用程序 icdab_gyro
我们将展示如何检测 iOS-on-Mac 的场景:
let info = ProcessInfo()
if #available(iOS 14.0, *) {
if info.isiOSAppOnMac {
print("We are an iOS app running on a Mac")
}
}
此外,当使用陀螺仪时
var motion = CMMotionManager()
并仅在可用时使用陀螺仪:
if motion.isGyroAvailable {
self.motion.gyroUpdateInterval = 1.0 / 60.0
self.motion.startGyroUpdates()
.
.