Swift底层原理-结构体、类和协议

在Objective-C底层原理-NSObject文章中,我们深入了解了Objective-C对象的底层实现。本文将探讨Swift中类和结构体的底层原理。 Swift数据结构的分类 Swift中的类根据是否继承自NSObject,在底层实现上存在显著差异: 继承自NSObject的Swift类:兼容Objective-C运行时,支持完整的Objective-C特性 纯Swift类:使用Swift原生运行时,性能更优但Objective-C互操作性受限 Swift中结构体是值类型,具有以下核心特征: 栈上分配内存(非逃逸情况) 值语义和完整拷贝 写时拷贝优化(COW) 静态方法派发 Swift类的底层实现 继承自NSObject的Swift类 当Swift类继承自NSObject时,必须兼容Objective-C的运行时系统,其底层实现与Objective-C对象高度一致。 内存布局 class Class: NSObject { var name: String var isMale: Bool var age: Int init(name: String, isMale: Bool, age: Int) { self.name = name self.isMale = isMale self.age = age super.init() } } 实例内存布局: 偏移量 内容 大小 说明 0x0-0x7 isa_t isa 8字节 指向类对象,包含优化的位域信息 0x8-0x17 String name 16字节 Swift String结构(64位系统) 0x18 Bool isMale 1字节 布尔值 0x19-0x1F (padding) 7字节 Swift编译器根据下一个字段(Int,8字节对齐要求)自动插入填充,确保Int字段从8的倍数地址开始 0x20-0x27 Int age 8字节 64位整数 0x28-0x2F (padding) 8字节 最终内存对齐填充 关键特性: ...

May 6, 2026

iOS 定时器的注意事项

iOS 开发中常用的定时器有 NSTimer、CADisplayLink 和 GCD Timer。它们各有特点,但在使用时都有一些需要注意的问题。 常见定时器类型 1. NSTimer 最常用的定时器,基于 RunLoop 实现: // 自动添加到当前 RunLoop 的 DefaultMode NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFired) userInfo:nil repeats:YES]; // 手动创建,需要手动添加到 RunLoop NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerFired) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; 2. CADisplayLink 与屏幕刷新率同步的定时器,适合做动画: CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)]; [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 3. GCD Timer 不依赖 RunLoop,精度更高: dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0); dispatch_source_set_event_handler(timer, ^{ NSLog(@"GCD Timer fired"); }); dispatch_resume(timer); 注意事项一:循环引用问题 问题描述 NSTimer 和 CADisplayLink 在使用 target-action 模式时,会强引用 target。如果 target 又持有定时器,就会形成循环引用: ...

May 2, 2026

耗电-网络优化

网络是iOS设备上仅次于屏幕的耗电大户。和其他模块不同,网络耗电的"坑"非常隐蔽——请求本身只持续几十毫秒,但无线电(Radio)会有10~20秒的"尾能耗"。本文从无线电能效模型出发,给出具体的网络优化手段。 一、无线电能效模型回顾 RRC状态机再看一眼 无线电不是"开就费电、关就不费电"这么简单。它在连接和空闲之间存在中间状态: stateDiagram-v2 [*] --> Idle: 初始/长时间空闲 Idle --> Connected: 发送/接收 Connected --> ShortDRX: 空闲~1秒 ShortDRX --> LongDRX: 空闲~5秒 LongDRX --> Idle: 空闲~10秒 Connected --> Connected: 持续通信 ShortDRX --> Connected: 新请求 LongDRX --> Connected: 新请求 状态 功耗 说明 Connected 最高(12W) 数据传输中 Short DRX 中等 周期性监听页 paging,仍持有连接 Long DRX 低 周期变长,仍在空中接口 Idle 极低(~几mW) 完全释放 一次请求从发起到彻底回到Idle大约 12~20秒。这个尾巴就是"Tail Energy"。 能效启示 10次请求,每次间隔2秒: - Radio持续处于Connected/DRX状态 ≈ 30秒 - 实际传输时间可能只有几百ms 10次请求合并成1次: - Radio活跃时间 ≈ 15秒(主要是尾巴) - 节省约50%~70%的Radio能耗 结论:网络能效的核心就是"减少Radio醒来的次数"。 ...

May 2, 2026

耗电

耗电(Energy Consumption)是移动应用质量的重要指标之一。一款耗电过快的App会直接影响用户的续航体验,轻则被用户吐槽,重则被系统标记为"耗电大户"(在iOS的"设置 → 电池"中显示),最终被用户卸载。 本系列文章从iOS功耗模型入手,系统性地介绍耗电问题的原理、检测、优化与治理。 为什么要关注耗电 iOS设备的电池容量有限(iPhone通常在3000~5000mAh),而应用的功耗会直接影响续航时间。苹果在iOS 8之后提供了"电池用量"统计入口,用户可以清楚地看到每个App消耗的电量比例。 常见的耗电投诉场景: 场景 用户感知 典型原因 后台待机一晚电量掉20% 手机放着不用也掉电 后台定位、后台音频、后台Fetch未合理释放 App使用1小时电量掉30% 使用过程电量下降过快 CPU长时间高占用、高频网络请求、GPU密集渲染 手机发烫 设备温度异常升高 CPU/GPU长期满载、持续定位、频繁屏幕唤醒 被系统标记为"后台活动"过高 系统提示"此App正在消耗大量电池" 后台任务未合理调度,违反能效规则 苹果对"耗电"非常敏感——从iOS 9开始引入低电量模式(Low Power Mode),从iOS 13开始加强后台任务的能效审查(BackgroundTasks框架),从iOS 15开始通过MetricKit将功耗数据开放给开发者。 iOS功耗模型概述 iOS设备的功耗来源可以分为以下几大类: flowchart TB Battery["电池总功耗"] Battery --> CPU["CPU计算、编解码"] Battery --> GPU["GPU渲染、图层合成"] Battery --> Screen["屏幕显示、亮度、刷新率"] Battery --> Network["网络蜂窝、WiFi、BLE"] Battery --> Location["定位GPS、基站、WiFi定位"] Battery --> Sensor["传感器加速度计、陀螺仪、气压计"] Battery --> Audio["音视频录制、播放、相机"] Battery --> Storage["存储磁盘IO"] Battery --> IO["外设蓝牙、NFC、UWB"] 不同硬件模块的功耗量级差异很大。参考苹果WWDC的数据,定位和蜂窝数据通常是"耗电大户",屏幕是"持续耗电大户",而CPU/GPU在计算密集时瞬时功耗最高。 模块 典型功耗等级 特点 屏幕 持续高 只要亮屏就持续耗电 蜂窝数据 高 搜索基站、维持连接都耗电 GPS定位 高 冷启动搜星耗电特别明显 CPU/GPU 中~高 计算量决定功耗 WiFi 中 比蜂窝省电 蓝牙(BLE) 低 低功耗蓝牙专为续航优化 传感器 低~中 高频采样会显著增加功耗 耗电问题的本质 耗电问题的本质可以用一个简单的公式表示: ...

May 8, 2026

iOS中的内存管理

本文将系统介绍iOS中的内存管理机制,从程序内存布局、引用计数原理、ARC机制到系统级内存管理,帮助你建立完整的iOS内存管理知识体系。 一、程序内存布局 内存区域划分 在iOS应用程序中,内存按照用途和管理方式分为以下几个区域: 高地址 ┌─────────────────────────────────────┐ │ 栈区 (Stack) │ ↓ 向低地址增长 │ 局部变量、函数参数、返回地址 │ ├─────────────────────────────────────┤ │ ↕ │ │ 动态分配区 │ │ ↕ │ ├─────────────────────────────────────┤ │ 堆区 (Heap) │ ↑ 向高地址增长 │ 动态分配的对象实例 │ ├─────────────────────────────────────┤ │ 全局/静态区 (BSS) │ │ 未初始化的全局变量和静态变量 │ ├─────────────────────────────────────┤ │ 数据区 (Data) │ │ 已初始化的全局变量和静态变量 │ ├─────────────────────────────────────┤ │ 常量区 (Rodata) │ │ 字符串常量等 │ ├─────────────────────────────────────┤ │ 代码区 (Text) │ │ 编译后的代码 │ └─────────────────────────────────────┘ 低地址 各区域的特点: ...

May 2, 2026

JetsamEvent 日志解读

JetsamEvent 是 iOS 唯一由系统官方生成、且专门记录"因内存原因终止进程"的日志。它不记录线程堆栈,却完整刻画了发生强杀那一刻整机的内存分布——这使它成为 OOM 归因、阈值建模、后台生命周期治理最权威的数据源。 本文聚焦 JetsamEvent 日志本身:存放位置、文件结构、字段含义、reason 类型、案例解读、自动化批量解析脚本,以及机型内存上限/iOS 版本差异等实战要点。OOM 监控治理的整体方法论见 崩溃-治理。 1. Jetsam 与 JetsamEvent 是什么 iOS 通过 XNU 内核的 memorystatus 子系统(俗称 Jetsam)做整机内存管理: 前台/后台进程按 Jetsam 优先级排序; 当系统整体可用物理页(vm_page_free_count)或单个进程 phys_footprint 触发阈值时,按优先级杀掉进程; 被杀时,内核在 /var/mobile/Library/Logs/CrashReporter/ 生成 JetsamEvent-yyyy-MM-dd-HHmmss.ips 文件,并通过 DiagnosticsReporting 暴露给 设置 → 隐私与安全性 → 分析与改进 → 分析数据。 需要理解两个关键结论: JetsamEvent 不是单个进程的崩溃日志,而是整机一次 Jetsam 事件的快照。 一个文件里往往包含几十个进程条目,只有其中一个(或少数)被真正杀掉;其余进程是作为"当时整机内存画像"被附带记录。 JetsamEvent 不含线程堆栈。 它的价值在于告诉你"谁占用了多少内存"和"为什么被杀",而不是"在哪一行代码被杀"。堆栈需要由 App 自己的 Memory Dump / MetricKit 补齐。 2. 获取方式 场景 路径 真机设置页查看 设置 → 隐私与安全性 → 分析与改进 → 分析数据,文件名 JetsamEvent-*.ips Xcode 连接真机 Window → Devices and Simulators → 选中设备 → View Device Logs sysdiagnose 包 解压后位于 crashes_and_spins/*.ips 越狱设备 /var/mobile/Library/Logs/CrashReporter/JetsamEvent-*.ips 自动上报 App 下次启动时扫描沙盒外的 Apple 日志目录不可行,只能依赖 MetricKit 的 MXCrashDiagnostic/MXAppExitMetric(iOS 14+) ⚠️ iOS 沙盒下 App 无法直接读取 /var/mobile/Library/Logs/ 的原始 ips 文件,线上规模化采集必须走 MetricKit。JetsamEvent 原始日志主要用于研发期抓真机、或用户反馈场景的手工分析。 ...

May 3, 2026

iOS中的数据库

数据持久化是移动端开发的核心能力之一。iOS提供了从轻量级KV存储到完整关系数据库的多层次方案。本文将从底层存储原理出发,系统性地梳理iOS中各类数据库与持久化技术的设计思想、实现机制和适用场景。 一、iOS数据持久化方案全景 iOS中常见的数据持久化方案,按数据复杂度和使用场景可分为以下几层: graph TD subgraph KV["轻量级KV存储"] A["UserDefaultsplist序列化"] B["Keychain加密存储"] C["MMKVmmap + protobuf"] end subgraph FILE["文件存储"] D["plist / JSON"] E["NSCoding / Codable归档"] end subgraph SQL["关系数据库"] F["SQLiteC语言API"] G["FMDBOC封装"] H["WCDBORM + 加密"] end subgraph ORM["ORM框架"] I["Core DataApple官方"] J["Realm零拷贝"] K["SwiftDataSwift原生"] end KV --> |"数据结构更复杂"| FILE FILE --> |"需要结构化查询"| SQL SQL --> |"需要对象映射"| ORM 方案 数据模型 查询能力 线程安全 加密 适用场景 UserDefaults KV (plist类型) Key查找 线程安全 无 用户偏好、简单配置 Keychain KV (Data) Key查找 线程安全 系统加密 密码、Token、证书 MMKV KV (protobuf) Key查找 线程安全 可选AES 高频读写的KV数据 plist/JSON文件 字典/数组 无 不安全 无 静态配置、缓存 NSCoding/Codable 对象图 无 不安全 无 对象序列化 SQLite 关系表 SQL 需手动处理 可选(SQLCipher) 结构化数据、复杂查询 FMDB 关系表 SQL FMDatabaseQueue 可选 SQLite的OC封装 WCDB 关系表+ORM SQL+ORM 自动管理 内置 高性能结构化存储 Core Data 对象图 NSPredicate NSManagedObjectContext 无 复杂对象关系 Realm 对象 类型安全查询 对象冻结 可选AES 高性能对象存储 SwiftData Swift对象 #Predicate宏 ModelContext 无 Swift原生数据持久化 二、轻量级KV存储 2.1 UserDefaults UserDefaults是iOS最常用的轻量级存储方案,底层将数据以plist格式序列化到磁盘。 ...

May 2, 2026

崩溃-Mach异常

本文深入介绍Mach异常的底层实现细节,包括端口操作、消息机制以及如何实现自定义的Mach异常处理器。 关于Mach异常的基础概念(Task、Thread、Port、Message)和异常端口的查找顺序,请参考 崩溃-原理。 Mach端口详解 端口权限 端口权限类型: ┌─────────────────────────────────────────────────────────────┐ │ 端口权限 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ MACH_PORT_RIGHT_SEND │ │ ├── 发送权限 │ │ ├── 可以向端口发送消息 │ │ └── 可以复制给其他任务 │ │ │ │ MACH_PORT_RIGHT_RECEIVE │ │ ├── 接收权限 │ │ ├── 可以从端口接收消息 │ │ ├── 只能有一个持有者 │ │ └── 持有者销毁时端口销毁 │ │ │ │ MACH_PORT_RIGHT_SEND_ONCE │ │ ├── 一次性发送权限 │ │ └── 发送一次后自动失效 │ │ │ │ MACH_PORT_RIGHT_PORT_SET │ │ ├── 端口集合权限 │ │ └── 可以同时监听多个端口 │ │ │ │ MACH_PORT_RIGHT_DEAD_NAME │ │ ├── 死名权限 │ │ └── 端口已销毁但名字仍存在 │ │ │ └─────────────────────────────────────────────────────────────┘ 端口操作 #include <mach/mach.h> // 分配新端口 mach_port_t port; kern_return_t kr = mach_port_allocate( mach_task_self(), // 当前任务 MACH_PORT_RIGHT_RECEIVE, // 接收权限 &port // 输出端口名 ); // 添加发送权限 kr = mach_port_insert_right( mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND // 创建发送权限 ); // 销毁端口 kr = mach_port_destroy(mach_task_self(), port); // 释放端口权限 kr = mach_port_deallocate(mach_task_self(), port); Mach消息 消息结构 // 消息头部 typedef struct { mach_msg_bits_t msgh_bits; // 消息类型和权限 mach_msg_size_t msgh_size; // 消息大小 mach_port_t msgh_remote_port; // 目标端口 mach_port_t msgh_local_port; // 本地端口(用于回复) mach_port_name_t msgh_voucher_port;// 凭证端口 mach_msg_id_t msgh_id; // 消息ID } mach_msg_header_t; // 完整消息示例 typedef struct { mach_msg_header_t header; // 可选的消息体 mach_msg_body_t body; // 可选的端口描述符 mach_msg_port_descriptor_t port; // 可选的数据 char data[256]; } MyMessage; 消息发送与接收 // 发送消息 kern_return_t send_message(mach_port_t port, const char *data, size_t len) { struct { mach_msg_header_t header; char data[256]; } message; message.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); message.header.msgh_size = sizeof(message); message.header.msgh_remote_port = port; message.header.msgh_local_port = MACH_PORT_NULL; message.header.msgh_id = 0; size_t copy_len = len < sizeof(message.data) ? len : sizeof(message.data); memcpy(message.data, data, copy_len); return mach_msg( &message.header, MACH_SEND_MSG, // 发送 sizeof(message), // 发送大小 0, // 接收大小 MACH_PORT_NULL, // 接收端口 MACH_MSG_TIMEOUT_NONE, // 超时 MACH_PORT_NULL // 通知端口 ); } // 接收消息 kern_return_t receive_message(mach_port_t port) { struct { mach_msg_header_t header; char data[256]; } message; kern_return_t kr = mach_msg( &message.header, MACH_RCV_MSG, // 接收 0, // 发送大小 sizeof(message), // 接收大小 port, // 接收端口 MACH_MSG_TIMEOUT_NONE, // 超时 MACH_PORT_NULL // 通知端口 ); if (kr == MACH_MSG_SUCCESS) { // 处理消息 printf("Received: %s\n", message.data); } return kr; } 异常端口操作 设置异常端口 #include <mach/mach.h> // 设置任务级异常端口 kern_return_t setup_task_exception_port(mach_port_t exception_port) { // 异常类型掩码 exception_mask_t mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT | EXC_MASK_SOFTWARE; return task_set_exception_ports( mach_task_self(), mask, exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE ); } // 获取当前异常端口 kern_return_t get_exception_ports(void) { exception_mask_t masks[EXC_TYPES_COUNT]; mach_msg_type_number_t count = EXC_TYPES_COUNT; mach_port_t ports[EXC_TYPES_COUNT]; exception_behavior_t behaviors[EXC_TYPES_COUNT]; thread_state_flavor_t flavors[EXC_TYPES_COUNT]; return task_get_exception_ports( mach_task_self(), EXC_MASK_ALL, masks, &count, ports, behaviors, flavors ); } 异常处理流程 完整的异常处理流程 flowchart TD A["1. 硬件检测到异常CPU执行非法操作,触发硬件中断"] --> B["2. 陷入内核CPU切换到内核态,执行异常处理程序"] B --> C["3. 内核构造异常消息创建包含异常信息的Mach消息"] C --> D["4. 发送到异常端口按照 Thread → Task → Host 顺序查找端口"] D --> E["5. 等待回复内核暂停线程,等待异常处理程序的回复"] E --> F{"6. 处理回复"} F -->|KERN_SUCCESS| G["恢复线程执行"] F -->|其他| H["转换为Unix信号或终止进程"] 异常消息格式 // 异常消息结构 typedef struct { mach_msg_header_t Head; // 消息体 mach_msg_body_t msgh_body; mach_msg_port_descriptor_t thread; // 发生异常的线程 mach_msg_port_descriptor_t task; // 发生异常的任务 // 异常信息 NDR_record_t NDR; exception_type_t exception; // 异常类型 mach_msg_type_number_t codeCnt; // 代码数量 int64_t code[2]; // 异常代码 } __Request__mach_exception_raise_t; // 异常回复消息 typedef struct { mach_msg_header_t Head; NDR_record_t NDR; kern_return_t RetCode; // 返回码 } __Reply__mach_exception_raise_t; 实现Mach异常处理器 完整示例 #include <mach/mach.h> #include <pthread.h> // 异常处理线程 static pthread_t exception_thread; static mach_port_t exception_port; // 保存原始异常端口 static mach_port_t original_exception_port; static exception_behavior_t original_behavior; static thread_state_flavor_t original_flavor; // 异常处理函数 static kern_return_t handle_exception( mach_port_t exception_port, mach_port_t thread, mach_port_t task, exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t code_count ) { // 获取线程状态(ARM64架构) // 注意:如需支持x86_64模拟器,需要使用x86_thread_state64_t和X86_THREAD_STATE64 #if defined(__arm64__) arm_thread_state64_t state; mach_msg_type_number_t state_count = ARM_THREAD_STATE64_COUNT; thread_get_state(thread, ARM_THREAD_STATE64, (thread_state_t)&state, &state_count); // 记录崩溃信息 printf("Exception: %d\n", exception); printf("Code: 0x%llx, 0x%llx\n", code[0], code[1]); printf("PC: 0x%llx\n", (unsigned long long)state.__pc); printf("LR: 0x%llx\n", (unsigned long long)state.__lr); #endif // 采集堆栈(需要自行实现) // collect_backtrace(thread); // 保存崩溃日志(需要自行实现) // save_crash_log(); // 返回失败,让系统继续处理(转换为信号) return KERN_FAILURE; } // 异常处理线程主函数 static void *exception_handler_thread(void *arg) { kern_return_t kr; while (1) { // 接收异常消息 struct { mach_msg_header_t head; mach_msg_body_t body; mach_msg_port_descriptor_t thread; mach_msg_port_descriptor_t task; NDR_record_t NDR; exception_type_t exception; mach_msg_type_number_t code_count; int64_t code[2]; int flavor; mach_msg_type_number_t state_count; natural_t state[224]; } request; struct { mach_msg_header_t head; NDR_record_t NDR; kern_return_t ret_code; } reply; // 接收消息 kr = mach_msg( &request.head, MACH_RCV_MSG | MACH_RCV_LARGE, 0, sizeof(request), exception_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL ); if (kr != MACH_MSG_SUCCESS) { continue; } // 处理异常 kr = handle_exception( exception_port, request.thread.name, request.task.name, request.exception, request.code, request.code_count ); // 发送回复 reply.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0); reply.head.msgh_size = sizeof(reply); reply.head.msgh_remote_port = request.head.msgh_remote_port; reply.head.msgh_local_port = MACH_PORT_NULL; // Mach IPC惯例:回复消息ID = 请求消息ID + 100 reply.head.msgh_id = request.head.msgh_id + 100; reply.NDR = NDR_record; reply.ret_code = kr; mach_msg( &reply.head, MACH_SEND_MSG, sizeof(reply), 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL ); } return NULL; } // 安装异常处理器 kern_return_t install_exception_handler(void) { kern_return_t kr; // 创建异常端口 kr = mach_port_allocate( mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exception_port ); if (kr != KERN_SUCCESS) return kr; // 添加发送权限 kr = mach_port_insert_right( mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND ); if (kr != KERN_SUCCESS) return kr; // 保存原始异常端口 exception_mask_t mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT; // 用于接收查询结果的数组 exception_mask_t masks_out[EXC_TYPES_COUNT]; mach_port_t ports_out[EXC_TYPES_COUNT]; exception_behavior_t behaviors_out[EXC_TYPES_COUNT]; thread_state_flavor_t flavors_out[EXC_TYPES_COUNT]; mach_msg_type_number_t count = EXC_TYPES_COUNT; kr = task_get_exception_ports( mach_task_self(), mask, // 输入:要查询的异常类型掩码 masks_out, // 输出:实际的掩码数组 &count, // 输入输出:数组大小 ports_out, // 输出:端口数组 behaviors_out, // 输出:行为数组 flavors_out // 输出:flavor数组 ); // 保存第一个有效的原始端口(简化处理) if (count > 0) { original_exception_port = ports_out[0]; original_behavior = behaviors_out[0]; original_flavor = flavors_out[0]; } // 设置新的异常端口 kr = task_set_exception_ports( mach_task_self(), mask, exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE ); if (kr != KERN_SUCCESS) return kr; // 创建异常处理线程 pthread_create(&exception_thread, NULL, exception_handler_thread, NULL); return KERN_SUCCESS; } 调试器检测 调试器(如LLDB)通过在Task(任务/进程)级别注册异常端口来优先拦截异常。了解这一机制有助于理解为什么调试时某些崩溃行为会有所不同。 ...

May 3, 2026

iOS反射

反射(Reflection)是指程序在运行时检查、访问和修改自身结构(类型、属性、方法等)的能力。在 iOS 开发中,Objective-C 和 Swift 分别提供了不同层次的反射支持:OC 依赖 Runtime 提供完整的动态反射能力,Swift 则通过 Mirror 提供只读的内省能力。 Objective-C 的反射能力 Objective-C 的反射能力完全建立在 Runtime 之上。Runtime 在运行时维护了完整的类型元数据(类对象、元类对象、方法列表、属性列表、成员变量列表、协议列表等),并通过一系列 C 函数 API 暴露给开发者,使得程序可以在运行时动态地查询和修改几乎所有类型信息。 关于 Runtime 的消息发送、消息转发、Method Swizzling、关联对象等核心能力,请参考 runtime。本节聚焦于 Runtime 中与"反射"直接相关的能力——即运行时的类型内省和动态操作。 类型内省 类型内省(Introspection)是反射中最基础的能力——在运行时查询一个对象的类型信息。 NSObject 提供的内省方法 NSObject 定义了一组内省方法,这些方法底层都依赖 Runtime 的 isa 指针和类型元数据来实现: id obj = [[NSMutableArray alloc] init]; // 类型判断 [obj isKindOfClass:[NSArray class]]; // YES — 判断是否是某个类或其子类的实例 [obj isMemberOfClass:[NSMutableArray class]]; // YES — 判断是否是某个类的直接实例 [obj conformsToProtocol:@protocol(NSCoding)]; // YES — 判断是否遵循某个协议 // 方法响应检查 [obj respondsToSelector:@selector(addObject:)]; // YES — 判断是否能响应某个消息 // 获取类型信息 NSStringFromClass([obj class]); // @"__NSArrayM"(真实的私有子类名) NSStringFromSelector(@selector(count)); // @"count" 需要注意 isKindOfClass: 和 isMemberOfClass: 的区别:前者沿继承链向上查找,后者只比较当前类。另外,[obj class] 返回的可能是私有子类(如类簇 NSArray 的实际类是 __NSArrayI 或 __NSArrayM),而 object_getClass(obj) 能获取真正的 isa 指向的类(KVO 场景下会是 NSKVONotifying_ 前缀的子类)。 ...

May 2, 2026

iOS编译原理

编译器架构:LLVM 与 Clang iOS 开发中的两种主要语言 Objective-C 和 Swift 都基于 LLVM 编译器基础设施。理解 LLVM 的架构是理解 iOS 编译原理的基础。 LLVM 的三段式架构 LLVM 采用经典的前端-中间表示-后端三段式设计,将编译过程解耦为三个独立的阶段: flowchart LR subgraph Frontend["前端(Frontend)"] direction TB A1["Clang(OC/C/C++)"] A2["Swift 前端"] end subgraph Middle["中间表示"] direction TB B1["LLVM IR"] end subgraph Backend["后端(Backend)"] direction TB C1["arm64(iOS真机)"] C2["x86_64(模拟器)"] end A1 --> B1 A2 --> B1 B1 --> C1 B1 --> C2 阶段 职责 iOS 中的实现 前端(Frontend) 词法分析、语法分析、语义分析,将源码转换为中间表示 Clang(OC/C/C++)、Swift 前端 中间表示(IR) 与语言无关、与平台无关的中间形式,承载优化 LLVM IR(Swift 还有额外的 SIL 层) 后端(Backend) 将 IR 转换为目标平台的机器码 arm64(真机)、x86_64(Intel 模拟器) 三段式架构的核心优势是解耦:新增一门语言只需实现一个前端,新增一个目标平台只需实现一个后端,所有语言和平台共享同一套优化基础设施。这正是 Apple 能同时支持 OC 和 Swift 两种语言、多种架构(arm64、x86_64)的技术基础。 ...

May 2, 2026