---
name: objc-ios-maintenance
description: "维护、审查、重构、现代化或调试 Objective-C iOS 项目。用于 .h/.m/.mm、UIKit/Auto Layout/滚动/渲染/启动、ARC/block/线程/CF bridge、KVC/KVO/runtime/swizzling、Swift 混编、Xcode 构建依赖、废弃 API/隐私合规、崩溃符号化/dSYM/MetricKit、EXC_BAD_ACCESS/OOM/watchdog、网络缓存、数据持久化、重构安全网、安全审计、推送后台、静态扫描工具链和新手 OC + UIKit 架构。"
---

# Objective-C iOS 维护与安全开发

当任务涉及 Objective-C iOS 代码库时使用这个 skill。维护既有项目时，默认采用小范围、兼容调用方、尊重运行时行为的方式；从零编写 OC + UIKit 功能或用户经验不清晰时，默认进入“新手安全层”，优先生成保守、可取消、主线程安全、少运行时魔法的代码。

## 边界

这个 skill 是 Objective-C iOS 旧项目维护的总入口，覆盖常见 UIKit、ARC、runtime、Swift 混编、废弃 API/合规适配、性能和崩溃问题。它按 `SKILL.md` + `references/` + `scripts/` + `assets/` 的通用 Agent Skill 结构组织，可被 Codex 和 Claude Code 读取。保持“一个总 skill + 多个 references”的组织方式；不要把它拆成多个子 skill，除非用户后续明确要求。

直接处理：

- Objective-C `.h`、`.m`、`.mm` 代码审查、修复、重构和现代化。
- UIKit view controller、cell、delegate/data source、Auto Layout、滚动、渲染和启动性能。
- ARC 所有权、block 循环引用、KVO/KVC、CoreFoundation bridge、runtime/category/swizzling 风险。
- Xcode 工程、`.xcconfig`、CocoaPods、SPM、静态库/闭源二进制、`-ObjC`/`-force_load`、category 符号裁剪、modulemap/PCH 和编译速度治理。
- App Store 合规适配、废弃 API 迁移、`PrivacyInfo.xcprivacy`、Required Reason API、ATT/IDFA、权限体系、刘海屏/安全区、Dark Mode、Dynamic Type 和 iPad 多窗口。
- 网络、缓存、本地数据持久化、并发、异步结果时序和崩溃边界。
- 无测试旧代码重构安全网、characterization test、OCMock、快照测试、Massive ViewController 拆分、单例和 `NSNotificationCenter` 收敛。
- 移动端安全审计：ATS 例外收敛、SSL pinning、硬编码密钥、WKWebView JS bridge 注入面、deep link、本地敏感数据和越狱检测边界。
- 推送与后台任务：APNs 证书到 p8 token auth 迁移、推送回调、notification extension、`beginBackgroundTask` 和 `BGTaskScheduler`。
- 静态扫描工具链：clang static analyzer / `xcodebuild analyze`、Infer、OCLint、`-Wall` warning 基线和 CI 分级治理。
- 新手或经验不明确场景下的 OC + UIKit 页面、列表、网络、model 和基础分层代码。

不直接处理：

- 大规模 Swift 重写或架构迁移，除非用户明确要求。
- 纯 Swift/SwiftUI 新功能设计；只处理与 Objective-C 维护或混编相关的部分。
- 用全局 swizzling/category 吞异常作为默认“防崩溃”方案；只有用户明确接受副作用时才讨论隔离方案。
- 没有证据的性能大改。先给出假设和验证方式，再做最窄改动。

## Skill 目录结构与维护入口

当前会话中，这个 skill 的维护目录为 `/Users/bobo/develop/objc-ios-maintenance`。后续对该 skill 的增补、翻译、脚本和资源维护都在这个目录下进行。若将 skill 复制到其他机器，按复制后的 skill 根目录解析相对路径。

按 Codex / Claude Code 兼容的 Agent Skill 结构维护如下：

```text
objc-ios-maintenance/
├── SKILL.md              # 必需：skill 入口、触发描述、核心工作流与引用路由
├── agents/
│   └── openai.yaml       # 推荐：用于 Codex/OpenAI UI 展示；Claude Code 可忽略
├── scripts/              # 可选：放可执行脚本；当前包含 Objective-C 风险巡检脚本
├── references/           # 可选：放按需读取的长文档；当前存放 Objective-C 维护专题资料
├── assets/               # 可选：放模板、图片、字体、示例工程等资源；当前包含 UIView 渲染性能分类和 weak proxy 模板
└── evals/                # 可选：放 skill 行为评测用例，用于后续回归验证
```

维护这个 skill 时，保持 `SKILL.md` 精简，只放触发信息、工作流、核心规则和引用路由；把较长的专题说明放入 `references/`，并从 `SKILL.md` 明确说明什么时候读取。

## 维护工作流

1. 先识别项目边界，再动手修改。
   - 确认文件是否涉及 ARC、非 ARC 编译标记、`.mm`、生成的 Swift 头文件、bridging header、category、swizzling、KVO、associated object 或 CoreFoundation 所有权。
   - 把 `.h` 文件当作 API 契约处理。修改 nullability、generics、selector 名称时要考虑 Swift 导入结果和所有调用方。
2. 先保持行为，再考虑现代化。
   - 除非任务需要，不要随意做 Swift 重写、架构迁移或替换 UIKit 流程。
   - 保持周围代码既有的 Objective-C 命名、delegate 模式和错误处理约定。
3. 每次修改都检查所有权和线程契约。
   - 确认属性修饰符、block 捕获、回调队列、UIKit 主线程访问和 CoreFoundation bridge 所有权。
4. 按 Objective-C runtime 规则在脑中编译一遍。
   - selector 拼写、category 冲突、KVC key、KVO 添加/移除平衡、swizzled 方法签名、Swift 生成名，和类型语法同样重要。
5. 用最窄的有效方式验证。
   - 优先使用既有 Xcode scheme、测试命令或目标构建命令。无法验证时，明确说明缺口和对应风险区域。

## 新手安全层

当用户明确说自己是新手、不懂 iOS、不熟 Objective-C/UIKit，或要求“从零写页面/搭架构/写基础功能”，以及用户经验水平不清晰时，默认进入新手安全模式，并读取 `references/beginner-uikit-architecture.md`。

新手安全模式的默认立场：

- 采用 MVC + MVVM-lite，小分层表达职责：ViewController 管生命周期和 UI 绑定，Service 管请求和取消，Model 管类型收敛，Cell 只做幂等渲染。
- 默认不使用全局 runtime crash guard、method swizzling、manual KVO、heavy associated object 或 `@try/@catch` 吞异常。
- 默认生成带 nullability、lightweight generics、正确 ARC 属性、可取消网络任务、主线程 UI 更新、稳定 cell reuse identifier 和输入校验的代码。
- 用户主动要求高级兜底时，先说明它是非默认历史包袱止血方案；只有用户接受风险，才进入 `references/runtime-crash-guard.md`。

## 任务路由决策树

先按用户任务选择最少 reference：

1. 如果用户是新手、经验不明确、从零写 OC + UIKit 页面/列表/网络功能，或让你搭基础架构，先读 `references/beginner-uikit-architecture.md`。
2. 如果任务是泛泛“审查这个 OC 文件/项目”，先读 `references/performance-diagnostics.md`，必要时运行 `scripts/scan_objc_risks.py`，再按命中类别读取具体 reference。
3. 如果涉及重构、无测试老代码、characterization test、OCMock、快照测试、Massive ViewController、单例或 `NSNotificationCenter` 泛滥，读 `references/refactoring-safety-net.md`。
4. 如果涉及安全审计、ATS 例外、明文 HTTP、SSL pinning、硬编码密钥、WKWebView JS bridge 注入面、deep link、本地敏感数据或越狱检测，读 `references/security-audit.md`。
5. 如果涉及 APNs、推送证书到 p8、device token、notification extension、静默推送、`beginBackgroundTask`、后台 URLSession 或 BackgroundTasks，读 `references/push-background.md`。
6. 如果涉及 clang static analyzer、`xcodebuild analyze`、Infer、OCLint、`-Wall`、几千条 warning、baseline、CI 静态扫描或警告分级治理，读 `references/static-analysis-toolchain.md`。
7. 如果涉及属性、delegate、block、timer、observer 或页面不释放，读 `references/memory-ownership.md`；涉及内存上涨、缓存、`NSCache`、`imageNamed`、图片 downsampling、大图解码或 `@autoreleasepool` 峰值，再读 `references/memory-leaks-performance.md`。
8. 如果涉及 completion、URLSession、GCD、NSOperation、取消或 UI 回调，读 `references/errors-async-threading.md`；涉及共享状态、死锁、串行队列重入、QoS 优先级反转、`OSSpinLock`、锁、GCD 线程膨胀、`atomic` 误用或乱序覆盖，再读 `references/concurrency-safety.md`。
9. 如果涉及 REST、分页、缓存、弱网重试、重复请求或网络 owner，读 `references/networking-caching.md`。
10. 如果涉及 `NSUserDefaults`、Keychain、文件读写、归档/反归档、Core Data、SQLite/FMDB、离线数据、数据迁移、备份策略或本地敏感数据保护，读 `references/data-persistence.md`。
11. 如果涉及 CF/CoreGraphics/CoreText/Security 对象释放，读 `references/corefoundation-bridging.md`。
12. 如果涉及 `EXC_BAD_ACCESS`、`SIGSEGV`、`KERN_INVALID_ADDRESS`、`objc_msgSend` 崩溃、野指针、Zombie、Address Sanitizer/ASan、Malloc Scribble、Guard Malloc、use-after-free、double free、over-release、`assign` 对象或 `__unsafe_unretained`，读 `references/dangling-pointer-diagnostics.md`。
13. 如果涉及 OOM、Jetsam、FOOM、memory warning、前台无 crash log 退出、App 被系统杀死、watchdog、`0x8badf00d`、启动/前后台切换超时或主线程长时间无响应，读 `references/oom-watchdog-diagnostics.md`。
14. 如果涉及未符号化 crash log、崩溃日志符号化、dSYM、UUID 不匹配、`atos`、Xcode Organizer、第三方 SDK dSYM、MetricKit、`MXMetricManager`、`MXDiagnosticPayload`、`MXCrashDiagnostic` 或 `MXHangDiagnostic`，读 `references/crash-symbolication-metrickit.md`。
15. 如果涉及崩溃治理、crash log、崩溃率、线上止血、集合 nil/越界、列表更新崩溃或崩溃分类，先读 `references/crash-prevention.md`，再按分类读取内存、线程、runtime、UIKit 或 CoreFoundation reference。
16. 如果涉及 KVC/KVO、category、associated object、swizzling 或动态 selector，读 `references/runtime-kvo-categories.md`；涉及崩溃边界，再读 `references/crash-prevention.md`。
17. 如果用户明确要求运行时崩溃兜底、防崩溃分类、完全消息转发、集合/KVO swizzling 止血，读 `references/runtime-crash-guard.md`。这是非默认方案，只用于历史包袱兜底。
18. 如果涉及 Swift 混编、bridging header、生成的 `-Swift.h` 或 Swift 导入质量，读 `references/swift-interop.md`。
19. 如果涉及 UIKit 生命周期、delegate/data source、cell 复用或旧页面维护，读 `references/legacy-uikit.md`。
20. 如果涉及滚动掉帧、cell 复用、异步图片、高度缓存或 prefetch，读 `references/scrolling-performance.md`；涉及 Auto Layout/Masonry 动态布局，再读 `references/layout-performance.md`。
21. 如果涉及圆角、阴影、mask、透明混合或 rasterize，读 `references/uikit-rendering-performance.md`。
22. 如果涉及启动、首屏、pre-main、dyld、动态库数量、`+load` 统计、SDK 初始化或启动热路径，读 `references/startup-performance.md`。
23. 如果涉及 Xcode 工程、`project.pbxproj`、target/scheme、`.xcconfig`、CocoaPods、SPM、静态库、闭源 `.a`、`xcframework`、`-ObjC`、`-force_load`、category 符号裁剪、头文件依赖、PCH、umbrella header、modulemap 或编译速度，读 `references/build-system-dependencies.md`。
24. 如果涉及 App Store 审核、废弃 API、`UIWebView`/`WKWebView`、`AddressBook`、`ALAssetsLibrary`、`UIAlertView`/`UIActionSheet`、`PrivacyInfo.xcprivacy`、Required Reason API、ATT/IDFA、相册 Limited、精确定位、安全区、Dark Mode、Dynamic Type 或 iPad 多窗口，读 `references/compliance-adaptation.md`。

## 引用资料路由

只读取当前任务需要的 reference：

- `references/memory-ownership.md`：属性修饰符、delegate 所有权、retain cycle、weak/strong dance、timer、notification、associated object 循环引用。
- `references/beginner-uikit-architecture.md`：新手安全层、OC + UIKit 保守架构、默认禁用项、ViewController/Service/Model/Cell 职责、可复制模板使用。
- `references/refactoring-safety-net.md`：无测试老代码重构安全网、characterization test、OCMock、快照测试、Massive ViewController 拆分、单例和 `NSNotificationCenter` 收敛。
- `references/security-audit.md`：ATS 例外、明文 HTTP、SSL pinning、硬编码密钥、WKWebView JS bridge 注入面、deep link、本地敏感数据和越狱检测。
- `references/push-background.md`：APNs 证书到 p8 token auth 迁移、device token、推送接收、notification extension、静默推送、`beginBackgroundTask`、BackgroundTasks 和后台 URLSession。
- `references/static-analysis-toolchain.md`：clang static analyzer、`xcodebuild analyze`、Infer、OCLint、warning baseline、CI 门禁和老项目警告分级治理。
- `references/errors-async-threading.md`：`NSError **`、completion handler、URLSession/GCD/NSOperation 约定、主线程 UI 更新。
- `references/corefoundation-bridging.md`：`CFBridgingRetain`、`CFBridgingRelease`、`__bridge`、`__bridge_transfer`、Create/Copy/Get 所有权规则。
- `references/runtime-kvo-categories.md`：KVC/KVO 崩溃边界、manual KVO、context 指针、category、associated object、method swizzling。
- `references/runtime-crash-guard.md`：运行时兜底、防崩溃分类、完全消息转发、集合/KVO swizzling 止血和不可 runtime 兜底边界。非默认方案，只用于历史包袱兜底。
- `references/swift-interop.md`：bridging header、module、生成的 `-Swift.h`、影响 Swift 导入的 nullability/generics、`NS_SWIFT_NAME`、`NS_REFINED_FOR_SWIFT`。
- `references/legacy-uikit.md`：view controller 生命周期、table/collection cell 复用、Auto Layout、delegate/data source 维护。
- `references/uikit-rendering-performance.md`：UIKit 渲染性能、离屏渲染、圆角、阴影、mask、透明混合、`shouldRasterize`、列表滚动视觉效果优化。
- `references/scrolling-performance.md`：UITableView/UICollectionView 滚动性能、cell 复用、复用标识符、预估行高、异步图片、图片 downsampling、图片预解码、约束复用、高度缓存、prefetch、列表刷新卡顿。
- `references/layout-performance.md`：Auto Layout 性能、约束创建/更新、动态高度、Masonry `remakeConstraints`、frame 混用、约束冲突。
- `references/startup-performance.md`：启动性能、pre-main、dyld、动态库数量、`+load` / `+initialize`、`AppDelegate` / `SceneDelegate`、首屏、SDK 初始化、启动热路径瘦身。
- `references/memory-leaks-performance.md`：内存上涨、页面不释放、图片内存、`imageNamed` 缓存语义、ImageIO downsampling、`NSCache`、`autoreleasepool`、timer/display link/observer 生命周期。
- `references/crash-prevention.md`：崩溃治理分层、分类矩阵、治理闭环、集合 nil/越界、类型校验、列表批量更新一致性、KVC/KVO 崩溃、动态 selector、全局防崩溃分类风险。
- `references/crash-symbolication-metrickit.md`：崩溃日志符号化、dSYM/UUID 匹配、dSYM 归档、第三方 SDK 符号、MetricKit 接入、`MXCrashDiagnostic` / `MXHangDiagnostic` 和线上诊断闭环。
- `references/dangling-pointer-diagnostics.md`：`EXC_BAD_ACCESS`、`SIGSEGV`、`objc_msgSend` 野指针崩溃、Zombie、ASan、Malloc Scribble、Guard Malloc、use-after-free、double free 和 CF/C/C++ 内存访问诊断。
- `references/oom-watchdog-diagnostics.md`：OOM、Jetsam、FOOM、memory warning、内存峰值、缓存膨胀、watchdog、`0x8badf00d`、启动/前后台切换超时和主线程卡死诊断。
- `references/concurrency-safety.md`：GCD、NSOperation、共享 mutable state、主队列 `dispatch_sync`、串行队列重入、QoS 优先级反转、`OSSpinLock` 到 `os_unfair_lock`/锁迁移、GCD 线程膨胀、`atomic` 边界、竞态、取消语义、异步结果时序、completion 队列契约。
- `references/networking-caching.md`：NSURLSession、请求取消、重复请求合并、缓存 key、弱网重试、分页刷新、网络回调 UI 安全。
- `references/data-persistence.md`：`NSUserDefaults`、Keychain 后台访问时机、文件存储、`NSCoding` 到 `NSSecureCoding`、Core Data 跨线程访问、轻量迁移失败兜底、SQLite/FMDB 线程安全与 WAL、离线数据、迁移、备份策略和本地敏感数据保护。
- `references/performance-diagnostics.md`：Instruments、Core Animation、Leaks、Zombies、Main Thread Checker、静态风险巡检和性能优化记录。
- `references/build-system-dependencies.md`：Xcode 工程、`project.pbxproj` 合并冲突、target/scheme 漂移、`.xcconfig` 分层、CocoaPods/SPM 混用、静态库 category 符号裁剪、`-ObjC` / `-force_load`、闭源 `.a` 到 `xcframework`、头文件/PCH/modulemap 和编译速度治理。
- `references/compliance-adaptation.md`：废弃 API 迁移、`UIWebView` 到 `WKWebView`、JS bridge 白名单、cookie/session、Contacts、Photos、隐私清单、Required Reason API、ATT/IDFA、权限体系、安全区、Dark Mode、Dynamic Type 和 iPad 多窗口。

## 标准输出格式

进行审查或修复建议时，优先使用下面结构，按任务复杂度裁剪：

- **结论**：一句话说明主要风险、根因假设或修改方向。
- **证据**：列出代码位置、调用路径、生命周期边界、线程队列或工具数据。没有实测数据时，明确标为“代码线索/待验证假设”。
- **问题**：按严重度和影响排序，说明为什么会造成崩溃、卡顿、泄漏或维护风险。
- **建议改法**：给出最小可行修改；涉及 API 契约、Swift 导入、runtime 或线程时说明兼容性影响。
- **验证方式**：说明应运行的测试、构建、Instruments、Memory Graph、Core Animation、脚本扫描或手工复现路径。
- **剩余风险**：列出无法验证、依赖业务语义或需要用户确认的地方。

如果用户要求直接改代码，完成后汇报修改文件、行为变化和已运行验证。不要把 `scripts/scan_objc_risks.py` 的命中结果直接当作确定缺陷；它只是 review 线索。

## 可用代码模板

- `assets/snippets/UIView+OCMPerformance.h`
- `assets/snippets/UIView+OCMPerformance.m`
- `assets/snippets/OCMWeakProxy.h`
- `assets/snippets/OCMWeakProxy.m`
- `assets/snippets/OCMCrashSafety.h`
- `assets/snippets/OCMCrashSafety.m`
- `assets/snippets/webview/OCMWebView.h`
- `assets/snippets/webview/OCMWebView.m`
- `assets/snippets/webview/OCMWebViewFactory.h`
- `assets/snippets/webview/OCMWebViewFactory.m`
- `assets/snippets/webview/OCMWebCookieCoordinator.h`
- `assets/snippets/webview/OCMWebCookieCoordinator.m`
- `assets/snippets/webview/OCMWebSecurityPolicy.h`
- `assets/snippets/webview/OCMWebSecurityPolicy.m`
- `assets/snippets/webview/OCMWeakScriptMessageDelegate.h`
- `assets/snippets/webview/OCMWeakScriptMessageDelegate.m`
- `assets/templates/beginner-uikit/OCMItem.h`
- `assets/templates/beginner-uikit/OCMItem.m`
- `assets/templates/beginner-uikit/OCMItemService.h`
- `assets/templates/beginner-uikit/OCMItemService.m`
- `assets/templates/beginner-uikit/OCMItemListViewModel.h`
- `assets/templates/beginner-uikit/OCMItemListViewModel.m`
- `assets/templates/beginner-uikit/OCMItemCell.h`
- `assets/templates/beginner-uikit/OCMItemCell.m`
- `assets/templates/beginner-uikit/OCMItemListViewController.h`
- `assets/templates/beginner-uikit/OCMItemListViewController.m`

当用户明确需要 UIView 渲染性能工具分类时，参考或复制这两个文件。复制到业务项目后，建议把 `OCM` 方法前缀替换为项目自己的前缀，避免 category 方法名冲突。不要把模板当作全局自动优化工具；它只提供显式调用的圆角、阴影、`shadowPath`、透明背景和 rasterize 辅助方法。

当用户需要处理 `NSTimer` / `CADisplayLink` 持有 target 导致页面不释放时，参考或复制 `OCMWeakProxy` 模板。weak proxy 只能打断 target 循环引用，仍要在生命周期边界调用 `invalidate`。

当用户需要处理集合 nil/越界、外部 JSON 类型收敛或后台回调更新 UI 时，参考或复制 `OCMCrashSafety` 模板。它只提供显式调用的 helper，不改变 Foundation/UIKit 全局行为；调用点仍要处理空数据和降级状态。

当用户需要迁移 `UIWebView` 到 `WKWebView`、统一 JS bridge 白名单或 cookie/session 边界时，参考或复制 `assets/snippets/webview/`。这组模板提供页面级 `OCMWebView` 子类、factory、cookie coordinator、安全白名单和 weak script message delegate；不要把 `OCMWebView` 做全局单例，也不要把业务路由、支付、分享等 native 能力塞进模板。

当用户是新手或从零写 OC + UIKit 列表/网络页面时，优先参考 `assets/templates/beginner-uikit/`。这些模板展示保守分层、nullability/generics、可取消网络、主线程 completion、稳定 reuse identifier、generation token 和外部数据类型收敛。复制后要替换 `OCM` 前缀，并贴合项目既有网络层和图片加载库。

## 可用脚本

- `scripts/scan_objc_risks.py`
- `scripts/test_scan_objc_risks.py`

当用户需要先盘点 Objective-C 项目的性能、崩溃和运行时风险时，可以运行该脚本。脚本输出是人工 review 线索，不是确定缺陷；不要机械替换所有命中项。

```bash
python3 scripts/scan_objc_risks.py /path/to/YourProject
python3 scripts/scan_objc_risks.py /path/to/YourProject --category rendering
python3 scripts/scan_objc_risks.py /path/to/YourProject --category build
python3 scripts/scan_objc_risks.py /path/to/YourProject --category compliance
python3 scripts/scan_objc_risks.py /path/to/YourProject --category persistence
python3 scripts/scan_objc_risks.py /path/to/YourProject --category runtime
python3 scripts/scan_objc_risks.py /path/to/YourProject --category security
python3 scripts/scan_objc_risks.py /path/to/YourProject --category background
python3 scripts/scan_objc_risks.py /path/to/YourProject --category toolchain
python3 scripts/scan_objc_risks.py /path/to/YourProject --min-level warning
python3 scripts/scan_objc_risks.py /path/to/YourProject --format json --max-findings 50
python3 scripts/scan_objc_risks.py /path/to/YourProject --fail-on-finding
```

维护扫描脚本后，运行 `python3 scripts/test_scan_objc_risks.py` 验证多行匹配、JSON 输出和 CI 失败开关。

## 核心规则

### 属性所有权

- 对被当前对象拥有的 Objective-C 对象使用 `strong`。
- 对 `NSString`、`NSAttributedString`、`NSArray`、`NSDictionary`、`NSSet`、`NSData`、`NSIndexSet` 以及其他具有值语义的对象使用 `copy`，因为调用方可能传入 mutable 子类。
- block 属性必须使用 `copy`。栈上的 block 一旦需要逃逸出当前作用域，就必须被复制。
- delegate、data source、父对象/反向引用、由其他对象图拥有的对象使用 `weak`。标量和 C struct 使用 `assign`。
- 除非为了明确的旧系统兼容，不要对 Objective-C 对象引用使用 `assign`；如果必须使用，写清楚生命周期假设。
- 默认使用 `nonatomic`，除非既有 API 明确承诺 atomic 属性语义。`atomic` 不等于对象状态线程安全。

### 新手默认安全规则

- 用户经验不清晰时，把用户当作需要安全护栏的新手来写代码；先给保守 MVC + MVVM-lite 结构，再根据现有项目收窄。
- 新手默认代码必须包含 nullability、lightweight generics、正确 property ownership、block `copy`、weak delegate、可取消异步任务和主线程 UI 更新。
- 新手默认不引入全局 swizzling、runtime 完全转发、manual KVO、复杂 associated object 或吞异常兜底。
- 新手默认把外部数据在 model/service 层收敛，不让 `NSNull`、错误类型、越界 index 或 nil 插入集合进入 UI 层。
- 新手默认列表代码必须注册 cell、使用稳定 reuse identifier、幂等配置、`prepareForReuse` 重置、异步结果检查稳定 model identifier。

### 循环引用

- 任何被持有的 block 都可能强捕获 `self`：block 属性、被对象保留的动画 block、operation 持有的 completion block、timer、display link、block 形式的 notification observer。
- 对可能晚于当前调用栈执行的异步回调，使用 weak/strong dance：

```objc
__weak typeof(self) weakSelf = self;
[self.service loadWithCompletion:^(id result, NSError *error) {
    __strong typeof(weakSelf) self = weakSelf;
    if (!self) {
        return;
    }
    [self handleResult:result error:error];
}];
```

- 保存并正确失效 repeating timer、display link、KVO observation 和 block observer token。弱捕获不能替代生命周期清理。
- 不要盲目弱捕获。当任务语义要求 owner 存活时，优先使用显式取消或由 operation 对象表达所有权。

### 可空性（Nullability）与轻量泛型

- 现代头文件使用 `NS_ASSUME_NONNULL_BEGIN` / `NS_ASSUME_NONNULL_END` 包裹，再把真实可空的位置标为 `nullable`。
- 为集合内容标注 lightweight generics：`NSArray<NSString *> *`、`NSDictionary<NSString *, NSNumber *> *`、`NSSet<MyModel *> *`。
- initializer、factory、fluent API 返回接收者类型时使用 `instancetype`。
- delegate 属性和参数要精确标注：`id<MyDelegate>`，再按实际情况加 `nullable` 或 `weak`。
- 对 `NSError **`，在显式 nullability 的头文件中优先写成 `NSError * _Nullable * _Nullable error`。

### 错误、回调与线程

- 遵循 Cocoa 同步错误风格：返回 `BOOL` 或 nullable object；只在失败时写入 `*error`，写入前必须检查 `error != NULL`。
- 使用稳定的 error domain、code 和有用的 `userInfo` key。不要一边返回部分成功值一边设置 error。
- 异步 API 使用一个 completion callback，并确保只调用一次。推荐形状为 `(ResultType _Nullable result, NSError *_Nullable error)`，并文档化或强制回调队列。
- UIKit 必须在主线程访问。URLSession 和很多后台回调默认不在主线程。

### CoreFoundation 桥接

- 用 Create/Copy/Get 命名规则判断所有权。
- 不转移所有权时使用 `__bridge`。
- 把已持有的 CF 对象交给 ARC 管理时使用 `__bridge_transfer` 或 `CFBridgingRelease`。
- 把 Objective-C 对象传给会接管所有权的 CF API 时使用 `__bridge_retained` 或 `CFBridgingRetain`。
- 同一个所有权声明不要既 bridge transfer 又手动 `CFRelease`。

### 运行时（Runtime）、KVC、KVO 与分类（Category）

- KVC 对未定义 key 会抛异常，也可能根据访问路径绕过 setter。除非序列化、绑定或动态表单必须使用 KVC，否则优先使用类型化访问。
- KVO 注册必须平衡。使用唯一的 static context 指针，并只在 `observeValueForKeyPath:ofObject:change:context:` 中处理自己的 context。
- manual KVO 修改值时，用匹配的 `willChangeValueForKey:` 和 `didChangeValueForKey:` 包裹。
- category 方法要加项目前缀，降低命名冲突。category 不能添加 ivar；associated object 需要稳定 static key 和正确 association policy。
- method swizzling 只作为最后手段。确实需要时，保留方法签名，用 `dispatch_once`，调用原实现，并记录受影响 selector。

### Swift 混编

- 不要在公开 `.h` 文件里 import 生成的 `ProductModuleName-Swift.h`。在头文件中使用 forward declaration，在 `.m` 文件里按需 import 生成的 Swift 头。
- app target 用 bridging header 把 Objective-C 暴露给 Swift。framework 使用 module map 或 umbrella header。
- 用 nullability、lightweight generics、`NS_SWIFT_NAME`、`NS_REFINED_FOR_SWIFT` 改善 Swift 导入质量。
- 除非同步更新所有 Objective-C 调用方，不要只为了 Swift 调用点更好看而修改 Objective-C selector 片段。

### 构建系统、依赖与模块化

- 线上 `unrecognized selector` 若 selector 来自静态库/Pod/闭源 `.a` 中的 category，优先检查最终 App/Extension target 的 `OTHER_LDFLAGS` 是否保留 `$(inherited)` 并包含必要的 `-ObjC`。
- `-force_load` 只对有证据的单个静态库使用，并记录原因；不要用全局 `-all_load` 作为长期兜底。
- 处理 `project.pbxproj` 冲突后必须跑 `plutil -lint`、`xcodebuild -list` 和关键 target/configuration 的 `-showBuildSettings`。
- `.xcconfig` 要分层清楚，避免 Xcode UI、Podfile post_install 和 xcconfig 多处重复设置同一项；可继承设置保留 `$(inherited)`。
- CocoaPods、SPM 和手动二进制混用时，盘点依赖唯一 owner、重复符号、资源 bundle、module 名、最低系统版本和 extension 差异。
- 闭源 `.a` 迁移 `xcframework` 时同步验证 headers、modulemap/umbrella header、dSYM、BCSymbolMaps、资源和 license。
- PCH 只放稳定低频公共头；公开 `.h` 尽量 forward declare，把具体 import 放到 `.m/.mm`，避免递归 Header Search Paths。
- 编译速度治理先建立 `xcodebuild -showBuildTimingSummary` 基线，再分别处理头文件依赖、PCH、script phase、Pods/SPM 缓存和链接方式。

### 废弃 API 与合规适配

- `UIWebView` 必须从源码、storyboard/xib、第三方 SDK 和最终二进制中清干净；迁移目标是 `WKWebView`，不是兼容包装旧 API。
- 不要把 `WKWebView` 本身做全局单例。可以共享 factory、`WKProcessPool`、`WKWebsiteDataStore` / cookie 协调器和白名单策略；每个页面仍持有自己的 web view。
- WKWebView JS bridge 必须有 host/scheme/message/payload 白名单，并在 teardown 时移除 script message handler。
- `AddressBook` 迁移到 Contacts 时重新设计授权、`keysToFetch`、联系人标识和变更通知；不要把 `ABRecordID` 当长期稳定 ID。
- `ALAssetsLibrary` 迁移到 Photos/PHPicker 时处理 Limited、add-only/read-write、取消选择和继续选择路径。
- `UIAlertView` / `UIActionSheet` 迁移到 `UIAlertController` 时，iPad action sheet 必须配置 popover 锚点。
- `PrivacyInfo.xcprivacy` 是 target 级交付物。App、framework 和第三方 SDK 都要盘点 collected data、tracking、tracking domains 和 Required Reason API。
- 访问 IDFA 或跨 App/网站 tracking 前必须接入 ATT；未授权时要有非追踪降级路径，不要用替代标识绕过。
- 相册、定位、通知、联系人等权限文案要和真实用途一致；Limited Photos 和 Reduced Accuracy 都要有 UI 降级。
- 安全区、Dark Mode、Dynamic Type 和 iPad 多窗口适配要在真机/模拟器矩阵验证，不能只靠旧布局常量。

### 旧 UIKit 代码维护

- 一次性 setup 放在 `viewDidLoad`；每次显示前需要刷新的内容放在 `viewWillAppear:`；依赖最终 frame 的布局放在 `viewDidLayoutSubviews`；cleanup/cancellation 放在与既有所有权匹配的生命周期方法中。
- 可复用 cell 的配置必须是幂等的。在 `prepareForReuse` 中重置临时状态并取消过期异步任务。
- 不要在 layout pass 中反复添加约束。代码创建约束前先设置 `translatesAutoresizingMaskIntoConstraints = NO`。
- delegate 和 data source 保持 weak。调用 optional delegate 方法前检查 `respondsToSelector:`。

### UIKit 渲染性能

- 优先优化热路径上的渲染问题，例如滚动 cell、频繁动画 view 和首屏大量重复卡片，不要机械消灭所有离屏渲染。
- 圆角和阴影通常分层处理：外层 view 负责 shadow 并设置 `shadowPath`，内层 view 负责 `cornerRadius` 和必要的裁剪。
- 阴影没有 `shadowPath` 时要重点审查，尤其是在 table/collection cell 中。
- `mask`、`masksToBounds`、透明混合和 `shouldRasterize` 都需要结合场景验证；不要把 UIView 分类写成自动修改所有 view 行为的万能工具。

### 滚动性能

- 先定位主线程、图片、布局、复用和数据刷新瓶颈，不要只凭感觉修改列表代码。
- cell 配置必须幂等：`prepareForReuse` 取消旧任务并重置状态，异步回调必须检查稳定 model identifier。
- 图片应按展示尺寸异步加载、后台解码、可取消、可缓存；不要在 cell 中同步读图或解码大图。
- 约束应创建一次并复用，动态变化优先改 `constant` 或 `active`；动态高度缓存要包含宽度、内容版本和字体环境。
- prefetch 只能做可取消的预热工作，不要在 prefetch completion 中直接更新 UI。

### 启动、内存与崩溃

- 崩溃治理先分类再修复：数据边界、UIKit 状态一致性、生命周期、线程与异步、KVC/KVO/runtime、所有权桥接、底层 C/CF/C++ 和 OOM 要分开判断。
- 每个高优先级崩溃都要有证据闭环：符号化栈、输入样本或状态路径、根因、最小修复、验证方式和残留风险。
- 线上 crash log 先验证 dSYM UUID 和符号化质量；未符号化或错符号的栈不能作为最终根因证据。
- MetricKit 用来补充 crash、hang、exit、CPU、内存和启动趋势；它不能替代 dSYM、完整 crash log 和可复现路径。
- 野指针和内存访问崩溃先选诊断工具再修：Objective-C 对象释放后收消息优先 Zombie，C/CF/C++ 越界或释放后访问优先 ASan，低频内存破坏再用 Malloc Scribble / Guard Malloc。
- OOM/Jetsam/FOOM 和 watchdog 不是普通异常：先区分泄漏、峰值、缓存膨胀、系统内存终止和主线程无响应，再选择内存或卡顿诊断工具。
- 启动路径先拆 pre-main、`didFinishLaunching`、首帧和可交互阶段；`+load` / `+initialize` 不做业务初始化、IO、数据库或大型 SDK 启动。
- 内存问题先区分泄漏、峰值过高和缓存膨胀；用 Memory Graph / Allocations 验证，不要只靠猜。
- 页面不释放优先检查 block、timer、display link、observer、KVO、delegate 和 associated object 的强引用链。
- 服务端、缓存和配置输入进入 model 层前做类型收敛；集合构造过滤 nil，数组访问检查边界。
- 不推荐用全局 swizzling 的“防崩溃分类”吞异常；优先修复调用边界和状态一致性。
- 运行时崩溃兜底是非默认方案，只用于历史包袱线上止血；unknown selector、集合越界/nil、KVO 不平衡、野指针、C/C++ 崩溃必须分开判断。

### 并发与诊断

- `atomic` 不等于线程安全；共享 mutable collection 必须通过串行队列、锁或同一 concurrent queue + barrier 保护。
- 避免主线程 `dispatch_sync(dispatch_get_main_queue(), ...)`；后台结果更新 UI 前切回主队列。
- 避免在串行队列内部 `dispatch_sync` 回同一队列；需要同步快照时使用 queue-specific key 或重新设计为异步边界。
- 高 QoS 或主线程路径不要同步等待低 QoS 队列、锁、semaphore 或 operation；优先预热、异步回调或统一依赖链 QoS。
- 旧代码中的 `OSSpinLock` 应迁移到 `os_unfair_lock`、`NSLock` 或串行队列；持锁期间只做短内存临界区，不调用外部 block/delegate、IO 或同步派发。
- 不要把大量阻塞任务无界丢到 global queue；批量 CPU/IO 工作使用有限并发队列、`NSOperationQueue.maxConcurrentOperationCount`、`NSURLSession` 或分批背压。
- 多个异步请求可能乱序返回时，用 generation token 或稳定 model identifier 防止旧结果覆盖新状态。
- 取消语义要覆盖网络、解析、回调和 UI 更新；weak self 不能替代取消。
- 性能优化先测量后修改；用 Time Profiler、Core Animation、Allocations、Leaks 或埋点验证结果。

### 网络、缓存、持久化与布局

- 请求 owner 要明确，页面退出或条件变化时取消；首屏不等待非必要网络完成。
- 同一资源短时间多处请求时，考虑合并 in-flight request；缓存 key 包含用户、参数、尺寸、版本和语言等上下文。
- 重试必须有上限、退避和幂等性判断；支付、下单、状态变更不要客户端盲目重放。
- `NSUserDefaults` 只存小体积非敏感偏好；token、session、password 和私钥进入 Keychain，并处理 `OSStatus`、accessibility、账号隔离和退出登录清理。
- 文件持久化要明确目录语义、备份策略、原子写、`NSFileProtection` 和错误返回；可重建缓存不要放在会备份的用户文档目录。
- 归档/反归档优先使用 `NSSecureCoding` 和 allowed classes；Core Data / SQLite 访问要遵守队列、事务、迁移和错误处理契约。
- Core Data 跨线程只传 `NSManagedObjectID` 或 DTO，不跨队列传 `NSManagedObject`；所有 context 访问进入自己的 `performBlock:` / `performBlockAndWait:`。
- 轻量迁移失败要区分可重建缓存库和不可重建用户数据；SQLite/FMDB 多线程读写优先用串行队列或 `FMDatabaseQueue`，启用 WAL 时同步考虑 busy timeout、checkpoint 和 `-wal`/`-shm` 备份。
- Auto Layout 固定约束只创建一次；状态变化优先改 constraint `constant` / `active`。
- Masonry 旧代码初始化用 `makeConstraints`，状态变化用 `updateConstraints`，避免在滚动热路径高频 `remakeConstraints`。

## 常见错误

- 所有权：block/value 属性不用 `copy`、delegate/timer/observer/KVO 未清理、associated object 隐藏循环引用或误用 `ASSIGN`。
- 线程：后台 completion 直接更新 UI，主队列/串行队列 `dispatch_sync` 死锁，`atomic` 被当成集合线程安全，阻塞任务无界丢到 global queue。
- Runtime：`+load` swizzling 无 `dispatch_once`/签名检查，category selector 冲突，消息转发伪造签名，KVC/KVO 边界靠吞异常兜底。
- 构建：静态库/Pod category 缺 `-ObjC`，`$(inherited)` 被覆盖，`project.pbxproj` 冲突后未核对 target/scheme/configuration。
- UIKit 性能：cell 复用不幂等，图片/约束/高度计算在滚动热路径同步执行，圆角阴影同层处理且缺 `shadowPath`。
- 启动与内存：启动同步初始化非首屏 SDK，`imageNamed:` 用于大图/下载图，原图先解码再缩小，无上限缓存或 `NSCache` 缺 cost/key。
- 数据与网络：外部 `id` 未做类型收敛，列表批量更新数量不一致，请求 owner/取消/去重/缓存 key/重试边界不清。
- 持久化：敏感数据进 `NSUserDefaults`，文件目录/备份/原子写错误，旧归档无 `NSSecureCoding`，Core Data/SQLite 队列和迁移失败路径不清。
- 合规与安全：只替源码漏二进制 `UIWebView`，WKWebView/JS bridge 无白名单，ATS/证书校验放开，客户端硬编码 secret 或 APNs p8 私钥。
- 后台与工具链：后台任务 completion/end 不成对，BackgroundTasks 配置不完整，静态扫描命中被当成确定缺陷或 warning baseline 直接阻断主干。

## 审查清单

- API 契约：头文件 nullability/generics/`instancetype`、Swift 导入、错误返回和 completion 队列是否清晰？
- 所有权与线程：属性修饰符、block/timer/observer/KVO、CF bridge、共享 mutable state、取消和主线程 UI 是否安全？
- Runtime 与崩溃：KVC/KVO/category/swizzling 是否有边界；crash log、dSYM/MetricKit、野指针、OOM/watchdog 是否按证据闭环分类？
- UIKit 与性能：生命周期、cell 复用、滚动、渲染、布局、启动、内存和缓存是否有热路径数据或工具验证？
- 构建与依赖：pbxproj/target/scheme/xcconfig、Pods/SPM/二进制、`-ObjC`/`-force_load`、PCH/modulemap 和 dSYM 是否可复现？
- 网络与持久化：请求 owner、取消、缓存 key、重试、Keychain、文件、Core Data/SQLite、迁移和本地敏感数据保护是否完整？
- 合规与安全：废弃 API、PrivacyInfo、Required Reason API、ATT/权限、WKWebView/JS bridge、ATS/pinning、secret 和 deep link 是否收敛？
- 后台与工具链：APNs、推送 completion、background task、BGTask、后台 URLSession、静态分析 baseline 和 CI 门禁是否有 owner 与回滚？
- 重构与新手：无测试旧代码是否先补安全网；新手/经验不明场景是否默认使用保守 UIKit 分层并避开 runtime 魔法？
