APM-B端平台设计

B 端 APM 平台的目标不是“展示所有数据”,而是让研发团队在最短路径内完成:发现问题、判断影响、定位原因、分配负责人、验证修复、防止再次劣化。 一、信息架构 flowchart TB Home["质量概览"] --> Stability["稳定性"] Home --> Performance["性能体验"] Home --> Network["网络"] Home --> Session["用户会话"] Home --> Release["版本发布"] Home --> Alert["告警中心"] Home --> Config["SDK 配置"] Stability --> Crash["Crash"] Stability --> Watchdog["Watchdog"] Stability --> FOOM["FOOM"] Performance --> Launch["启动"] Performance --> View["页面"] Performance --> Freeze["卡顿/掉帧"] Performance --> Resource["资源"] Release --> Gray["灰度对比"] Release --> Gate["发布门禁"] Alert --> Rule["规则"] Alert --> Notify["通知"] Alert --> Silence["静默"] 一级导航不要按技术实现命名,比如 Kafka、ClickHouse、符号化任务;应该按用户任务命名,比如稳定性、性能体验、网络、发布、告警。 二、首页质量概览 首页回答三个问题: 当前版本稳不稳? 过去一段时间有没有变差? 最应该处理的 Top 问题是什么? 核心卡片: ...

May 7, 2026

死锁(Deadlock)原理、常见场景与治理

死锁是 iOS 多线程开发中最隐蔽、也最致命的一类稳定性问题。它不像越界、空指针那样"立刻崩溃给你看",而是表现为主线程"卡住"——用户能感受到界面无响应、无法交互,最终被 iOS watchdog 强杀(0x8BADF00D),落在 Apple 崩溃日志里归为 EXC_CRASH (SIGKILL) 或卡死(Hang)。 本文系统梳理死锁的四个必要条件、iOS 生产环境中常见的发生场景,并结合 Apple 官方工具(TSan、Main Thread Checker、MetricKit)与业界最新实践(Sentry App Hangs、字节 Heimdallr、Swift Concurrency 的反思)讨论如何检测与治理。 一、死锁是什么 死锁(Deadlock)指两个或多个线程因争夺资源(锁、队列、信号量等)而互相等待,导致所有相关线程永久阻塞、无法推进的状态。 与死锁容易混淆的几个概念: 名称 核心特征 典型例子 死锁 Deadlock 多个线程循环等待对方持有的资源,永久阻塞 线程 A 持 lockA 等 lockB;线程 B 持 lockB 等 lockA 活锁 Livelock 线程不阻塞但也不推进,不停重试互相让步 两个线程都检测到冲突就回退重试,永远撞在一起 饥饿 Starvation 低优先级线程长期分不到资源 高优先级线程持续抢占 CPU,导致低优先级线程持锁永远运行不完 优先级反转 Priority Inversion 低优先级持锁 + 高优先级自旋等锁 + 中优先级抢 CPU,高优先级被间接阻塞 OSSpinLock 被废弃的根本原因 死循环 Busy Loop 单线程进入无限循环,CPU 占用 100% while(true) 忘记 break 卡顿 Hang 主线程执行时间超过阈值(几百 ms~几秒) 主线程做网络、解压大图、Core Data 大量 fetch 卡死 Watchdog 主线程卡住数秒(启动约 20s、前后台切换约 10s),触发系统强杀 死锁导致的卡死是最严重的一种 关键区别:死循环线程 CPU 占用高、处于 RUNNING 态;死锁线程 CPU 占用为 0、处于 WAITING/BLOCKED 态并被换出。这一点正是线上死锁自动判定的核心依据(见"检测"章节)。 ...

May 4, 2026

AFNetworking 源码导读

本文基于 AFNetworking 4.0.1(2020 年发布,仓库最后一次迭代)源码进行分析。AFNetworking 虽然已进入"稳定休眠"状态,但它作为 iOS 网络层的教科书级实现,其 NSURLSession 封装、HTTPS 校验、Multipart 流式上传、Method Swizzling 等设计思路至今仍值得每一位 iOS 工程师学习。源码仓库:AFNetworking/AFNetworking。 一、整体架构 AFNetworking 的整个库只有 7 个核心类,按职责划分为"核心会话"、“序列化”、“安全”、“可达性”、“UIKit 扩展"五个子模块(对应 CocoaPods Subspec 拆分): graph TB subgraph "核心会话层 (NSURLSession 封装)" A["AFURLSessionManagerNSURLSession 代理总线"] B["AFHTTPSessionManagerHTTP 便利方法 GET/POST/..."] C["AFURLSessionManagerTaskDelegate每个 Task 的代理持有者"] end subgraph "序列化层 (Serialization)" D["AFHTTPRequestSerializerURL 编码 / 头部 / User-Agent"] E["AFJSONRequestSerializerAFPropertyListRequestSerializer"] F["AFStreamingMultipartFormDataAFMultipartBodyStream (流式上传)"] G["AFHTTPResponseSerializer+ JSON/XML/Image/PropertyList"] end subgraph "安全层 (Security)" H["AFSecurityPolicySSL Pinning (None/Certificate/PublicKey)"] end subgraph "可达性层 (Reachability)" I["AFNetworkReachabilityManager基于 SCNetworkReachability"] end subgraph "UI 扩展 (UIKit+AFNetworking)" J["UIImageView+AFNetworkingUIButton+AFNetworkingUIActivityIndicatorView+AFNetworking ..."] end B --> A A --> C A --> D A --> G A --> H A --> I D --> E D --> F J --> B 源码目录一览(AFNetworking 4.0.1): ...

May 2, 2026

App启动流程

启动类型概览 iOS应用启动分为三种类型: 启动类型 描述 特点 冷启动(Cold Launch) App完全不在内存中,需要从头开始加载 耗时最长,需要完整执行所有启动流程 热启动(Warm Launch) App在后台被挂起(Suspended),重新进入前台 最快,只需恢复App状态,不需要重新创建进程 预热启动(Pre-warm Launch) 系统预测用户可能启动App,提前在后台执行部分启动流程 iOS 15+引入,介于冷启动和热启动之间 graph LR subgraph "冷启动(耗时最长)" A1[创建进程] --> A2[加载dylib] A2 --> A3[Rebase/Bind] A3 --> A4[+load] A4 --> A5[main] A5 --> A6[首帧渲染] end subgraph "预热启动(iOS 15+)" B1[系统提前完成(后台):创建进程 → 加载dylib → Rebase/Bind] --> B2[用户点击后执行:+load → main → 首帧渲染] end subgraph "热启动(最快)" C1[唤醒进程] --> C2[WillEnterForeground] C2 --> C3[DidBecomeActive] C3 --> C4[恢复UI] end 一、冷启动(Cold Launch) 冷启动是最完整的启动流程,也是启动优化的主要关注点。 冷启动流程概览 flowchart TD Start([App 冷启动流程]) --> PreMain subgraph PreMain["Pre-main 阶段"] direction TB A[加载可执行文件] --> B[加载动态库(包括 Swift Runtime)] B --> C[Rebase & Bind] C --> D[ObjC Runtime 初始化(类注册、Category 附加)] D --> D2[Swift Runtime 元数据注册(类型元数据、协议遵循表)] D2 --> D3[调用 +load 方法] D3 --> E[执行 Initializers(C++静态构造函数、__attribute__)] end PreMain --> MainPhase subgraph MainPhase["main 阶段"] direction TB F[main函数] --> G[UIApplicationMain] G --> H[AppDelegate回调] H --> I[首帧渲染(创建Window、RootViewController、首屏UI)] end MainPhase --> End([启动完成]) Pre-main 阶段 Pre-main阶段是指从用户点击App图标到main函数执行之前的过程,这个阶段主要由dyld(动态链接器)负责。 ...

May 2, 2026

Instruments详解

Instruments 是 Xcode 内置的性能分析套件,基于 DTrace/Apple Trace 基础设施构建,可以对 iOS、iPadOS、macOS、watchOS、tvOS、visionOS 的应用与系统进行 CPU、内存、图形、能耗、网络、I/O 等各个维度的观测。 Xcode 26(WWDC25)对 Instruments 做了近年来最大规模的升级,重点包括: Power Profiler:全新的能耗分析工具,支持 Tethered 与 Passive 两种录制模式。 下一代 SwiftUI instrument:基于 Cause & Effect Graph 可视化状态变更到视图更新的因果链。 Processor Trace:基于 Apple Silicon 硬件特性的"全量指令级"采集(Xcode 16.3 引入,26 完善)。 CPU Counters 重做:引入 Bottleneck Analysis 方法论与 CPU Bottlenecks 模板。 Foundation Models instrument:为 FoundationModels 框架提供 Prompt / Asset Loading / Inference 分阶段观测。 Animation Hitches 重做:修正多显示器场景下的统计,数据量显著减少,处理更快。 UI 重设计:主菜单精简、Settings 页面重写、Track 次级菜单、Launch/Attach 环境变量配置。 一、Instruments 基础 1.1 启动 Instruments 的几种方式 方式 使用场景 Xcode Product → Profile(⌘I) 日常开发,自动以 Release 模式编译并附加符号 Xcode Debug Navigator 中的图表 粗略观测 CPU / Memory / Disk / Network / Energy 直接打开 Instruments.app 对已安装的 App、系统进程或已录制的 .trace 文件进行分析 命令行 xcrun xctrace record CI / 自动化性能回归 设备端 Developer Settings 的 Power Profiler 离线、无 Mac 场景采集能耗数据 Xcode 26 中,Product → Profile 会按默认 scheme 的 Profile 构建配置(通常是 Release + -O),这与 Debug 模式的数据差异很大,做性能评估时必须用 Profile 构建。 ...

May 2, 2026

MVC架构详解

什么是MVC MVC(Model-View-Controller)是Apple官方推荐的iOS应用架构模式,也是最基础、最常见的架构。它将应用分为三个核心组件: Model(模型):负责数据和业务逻辑 View(视图):负责界面展示 Controller(控制器):负责协调Model和View MVC的结构(Apple MVC) graph TD V[ViewUIView] -->|用户事件| C[ControllerUIViewController] C -->|更新UI| V C <-->|读写数据| M[Model数据层] 在Apple的MVC中,View和Model之间不能直接通信,所有交互都必须通过Controller中转。 Model(模型层) Model负责: 数据的存储和管理 业务逻辑处理 网络请求和数据持久化 数据验证 // Model示例 struct User { let id: Int let name: String let email: String var isValidEmail: Bool { let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" let emailPredicate = NSPredicate(format: "SELF MATCHES %@", emailRegex) return emailPredicate.evaluate(with: email) } } class UserService { func fetchUser(id: Int, completion: @escaping (Result<User, Error>) -> Void) { // 网络请求逻辑 } } View(视图层) View负责: ...

May 2, 2026

编译优化-Bazel方案

当 iOS 工程的规模突破 CocoaPods / Xcode 原生构建体系的舒适区,Bazel 就成为下一代构建系统的首选。字节跳动的头条、抖音、Airbnb、Uber、Lyft、Pinterest、Square、Bilibili 等都已把 iOS 工程迁到 Bazel。本文介绍 Bazel 的核心原理、在 iOS 上的生态(rules_apple / rules_swift / rules_xcodeproj)以及大厂落地实践。 为什么是 Bazel CocoaPods + Xcode 在超大工程上的固有瓶颈: 粗粒度依赖:以 Pod / Target 为单位,增量编译颗粒粗 隐式依赖:Build Phases 隐含顺序,难以沙箱化 难以远程缓存:编译环境非 hermetic,hash 易变 难以远程执行:Xcode Build System 绑定本地 macOS Bazel 针对这些问题从设计之初就提供了: 能力 Bazel Xcode/CocoaPods 依赖粒度 文件级 Target 级 依赖显式化 必须声明 部分隐式 Sandbox 默认开启 无 远程缓存 原生 需第三方(ccache/Rugby) 远程执行 原生 不支持 跨语言 一流(Swift/OC/C++/Go/Rust/JS) 主要 Apple 平台 多平台 天生 iOS/macOS 为主 Bazel 核心概念 Workspace 与 Package WORKSPACE # 仓库根,声明外部依赖 foo/ ├── BUILD.bazel # package,本目录的构建声明 ├── main.swift └── util/ └── BUILD.bazel # 子 package Workspace:整个 Bazel 仓库,一个 WORKSPACE 文件一个仓 Package:任何包含 BUILD.bazel 的目录 Target:BUILD 文件里的一个 rule 调用,比如 swift_library(name = "foo") Label:Target 的全局 ID,形如 //foo/util:util Rule Rule 是构建函数,输入 sources + deps,输出 artifacts: ...

May 2, 2026

APM-C端SDK架构

iOS APM SDK 是整个系统的数据入口。它运行在真实用户设备上,和业务 App 共进程、共资源、共生命周期,所以设计目标不是“能力越多越好”,而是 低开销、可控制、可降级、可追责、可合规。 一、SDK 职责边界 SDK 应该做: 采集 Crash、Watchdog、FOOM、卡顿、启动、网络、页面、MetricKit、业务 Trace 等现场。 生成并维护 session_id、view_id、action_id、resource_id、trace_id 等关联 ID。 做轻量预处理:去重、聚合、脱敏、采样、压缩、加密。 按数据价值分级落盘和上报。 接收远程配置,动态控制模块开关、采样率、阈值和熔断。 SDK 不应该做: 复杂 OLAP 查询。 大规模归因计算。 服务端符号化。 跨用户聚合。 长时间 CPU 采样或高频全量堆栈采样。 未经允许采集请求 body、用户输入、定位、通讯录等敏感数据。 二、分层架构 flowchart TB API["Public APIstart / identify / track / trace / breadcrumb"] Core["SDK Core生命周期 / 插件管理 / 远程配置 / 采样 / 隐私"] Context["Context ManagerApp / Device / User / Session / View / Trace"] Plugins["Plugin LayerCrash / Watchdog / FOOM / FPS / Launch / Network / MetricKit / Business"] Processor["Event Processor标准化 / 脱敏 / 聚合 / 去重 / 优先级"] Store["Local Storemmap / WAL / SQLite / 文件队列"] Uploader["Uploader批量 / 压缩 / 加密 / 重试 / 熔断"] API --> Core Core --> Context Core --> Plugins Plugins --> Processor Context --> Processor Processor --> Store Store --> Uploader Uploader --> Core 推荐模块: ...

May 7, 2026

Alamofire源码导读

Alamofire 是 Swift 社区最广泛使用的 HTTP 网络库,由 Alamofire Software Foundation 维护,在 GitHub 已收获 42k+ star。它在 URLSession 之上构建了一整套链式 API、拦截器、认证、证书校验、重试与响应序列化等能力。本文基于最新版本 v5.11.2(2026 年 4 月发布)源码进行分析,覆盖 Swift 6 严格并发、async/await、WebSocket、OfflineRetrier 等最新特性。 一、整体架构 Alamofire 采用中央调度 + 状态机 + 协议导向的设计,核心角色分工清晰: graph TB subgraph "入口层" AF["AF(Session.default)"] end subgraph "调度层" S["Session统一调度器"] SD["SessionDelegateURLSession 桥接"] end subgraph "请求层" R["Request (基类)状态机 + 生命周期"] DR["DataRequest"] DLR["DownloadRequest"] UR["UploadRequest"] DSR["DataStreamRequest"] WSR["WebSocketRequest"] end subgraph "拦截层" RA["RequestAdapter请求改写"] RR["RequestRetrier失败重试"] RI["RequestInterceptor= Adapter + Retrier"] end subgraph "服务层" RS["ResponseSerializer响应序列化"] STE["ServerTrustEvaluating证书/公钥校验"] EM["EventMonitor事件监控"] MP["MultipartFormData表单编码"] end subgraph "系统层" US["URLSession / URLSessionTask"] end AF --> S S --> SD S -->|创建| R R --> DR & DLR & UR & DSR & WSR S -.使用.-> RI RI -.= .-> RA & RR R -.序列化.-> RS SD -.校验.-> STE S -.通知.-> EM UR -.构建.-> MP SD <-->|delegate| US R -->|执行| US 源码目录(Source/,总计约 17000 行纯 Swift 代码): ...

May 2, 2026

Block底层原理

基础概念 Block 是 Apple 在 C 语言基础上扩展的语法特性(也称为 Closure / 闭包),它允许将一段代码和其执行时需要的上下文环境封装为一个可传递的对象。Block 在 Objective-C 和 C/C++ 中均可使用,是 GCD、动画 API、回调等场景的核心机制。 // Block 的声明与调用 void (^myBlock)(NSString *) = ^(NSString *name) { NSLog(@"Hello, %@", name); }; myBlock(@"World"); 一个核心问题是:Block 是函数指针还是对象? 答案是——Block 本质上是一个 OC 对象。它的底层是一个包含 isa 指针的 C 结构体,满足 OC 对象的基本条件;同时它也包含一个函数指针(FuncPtr),指向 Block 实际执行的代码。因此可以说,Block 是一个用对象形式包装了函数指针和捕获变量的特殊 OC 对象。 底层数据结构 通过 clang -rewrite-objc 可以将 Block 语法转换为 C++ 代码,观察其底层结构。这个转换虽然不完全等同于最终编译产物,但准确反映了 Block 的数据布局和调用机制。 编译器转换示例 对以下源码: int main() { int a = 10; void (^block)(void) = ^{ NSLog(@"%d", a); }; block(); return 0; } Clang 会将其转换为如下结构: ...

May 2, 2026