卡顿-TableView优化

列表滚动是用户最敏感的交互场景之一。本文介绍UITableView和UICollectionView的性能优化方案。 列表卡顿的常见原因 flowchart TB subgraph frame["每一帧(16.67ms内)需要完成"] A["1. 计算可见Cell"] --> B["2. 复用或创建Cell"] B --> C["3. 配置Cell内容"] C --> C1["设置文本"] C --> C2["加载图片"] C --> C3["计算布局"] C --> C4["渲染视图"] C1 --> D["4. 提交渲染"] C2 --> D C3 --> D C4 --> D end D --> E["⚠️ 任何一步超时都会导致掉帧"] 常见问题 问题 原因 影响 高度计算慢 复杂布局、Auto Layout 滚动卡顿 Cell配置慢 大量视图操作、图片加载 滚动卡顿 复用失效 未正确注册、identifier错误 内存暴涨、卡顿 离屏渲染 圆角、阴影 GPU瓶颈 图片加载 主线程解码 滚动卡顿 Cell复用机制 正确使用复用 class OptimizedTableViewController: UITableViewController { private let cellIdentifier = "OptimizedCell" override func viewDidLoad() { super.viewDidLoad() // 注册Cell类(推荐) tableView.register(OptimizedCell.self, forCellReuseIdentifier: cellIdentifier) // 或注册Nib // tableView.register(UINib(nibName: "OptimizedCell", bundle: nil), forCellReuseIdentifier: cellIdentifier) } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // 使用dequeueReusableCell(带indexPath的版本会自动创建) let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! OptimizedCell // 配置Cell let item = items[indexPath.row] cell.configure(with: item) return cell } } Cell的prepareForReuse class OptimizedCell: UITableViewCell { private let titleLabel = UILabel() private let avatarImageView = OptimizedImageView() private var currentTask: URLSessionTask? override func prepareForReuse() { super.prepareForReuse() // 重置状态 titleLabel.text = nil avatarImageView.image = nil // 取消进行中的任务 currentTask?.cancel() currentTask = nil avatarImageView.cancelLoading() } func configure(with item: Item) { titleLabel.text = item.title // 异步加载图片 avatarImageView.setImage(from: item.avatarURL, placeholder: UIImage(named: "placeholder")) } } 高度缓存 问题:每次都计算高度 // 问题代码:每次滚动都重新计算 func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let item = items[indexPath.row] return calculateHeight(for: item) // 耗时操作 } 解决方案1:预计算并缓存 class HeightCachedTableViewController: UITableViewController { private var items: [Item] = [] private var heightCache: [IndexPath: CGFloat] = [:] func setItems(_ newItems: [Item]) { items = newItems heightCache.removeAll() // 预计算高度(可以在后台线程) precomputeHeights() tableView.reloadData() } private func precomputeHeights() { let width = tableView.bounds.width for (index, item) in items.enumerated() { let indexPath = IndexPath(row: index, section: 0) heightCache[indexPath] = calculateHeight(for: item, width: width) } } private func calculateHeight(for item: Item, width: CGFloat) -> CGFloat { // 计算文本高度 let textHeight = item.content.boundingRect( with: CGSize(width: width - 32, height: .greatestFiniteMagnitude), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [.font: UIFont.systemFont(ofSize: 16)], context: nil ).height return ceil(textHeight) + 60 // 加上其他元素的高度 } override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return heightCache[indexPath] ?? UITableView.automaticDimension } // 数据更新时更新缓存 func updateItem(at indexPath: IndexPath, with item: Item) { items[indexPath.row] = item heightCache[indexPath] = calculateHeight(for: item, width: tableView.bounds.width) tableView.reloadRows(at: [indexPath], with: .automatic) } } 解决方案2:使用estimatedHeight class EstimatedHeightTableViewController: UITableViewController { private var heightCache: [IndexPath: CGFloat] = [:] override func viewDidLoad() { super.viewDidLoad() // 设置估算高度(重要!) tableView.estimatedRowHeight = 100 tableView.rowHeight = UITableView.automaticDimension } override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { // 缓存实际显示的高度 heightCache[indexPath] = cell.bounds.height } override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { // 优先返回缓存的高度 return heightCache[indexPath] ?? 100 } } 解决方案3:固定高度 // 如果所有Cell高度相同,直接设置固定值 override func viewDidLoad() { super.viewDidLoad() tableView.rowHeight = 80 // 固定高度,性能最好 tableView.estimatedRowHeight = 0 // 关闭估算 } 异步渲染 基本原理 将复杂的渲染工作移到后台线程: ...

May 2, 2026

卡顿-主线程优化

主线程阻塞是卡顿最常见的原因。本文介绍如何优化主线程的工作,减少阻塞时间。 主线程的职责 主线程(UI线程)负责: mindmap root((主线程职责)) 事件处理 触摸事件 手势识别 UI更新 布局计算 视图绘制 动画执行 Core Animation UIView动画 系统回调 生命周期 AppDelegate 定时器 NSTimer CADisplayLink 通知处理 NotificationCenter KVO 原则:主线程应该只做UI相关的轻量级工作 常见的主线程阻塞场景 1. 耗时计算 // 问题代码:在主线程进行复杂计算 func processData() { let result = heavyComputation(data) // 阻塞主线程 updateUI(with: result) } // 优化后:异步计算 func processDataAsync() { DispatchQueue.global(qos: .userInitiated).async { let result = self.heavyComputation(self.data) DispatchQueue.main.async { self.updateUI(with: result) } } } 2. 文件I/O // 问题代码:主线程读写文件 func loadConfig() { let data = try? Data(contentsOf: configURL) // 阻塞 parseConfig(data) } // 优化后:异步I/O func loadConfigAsync() { DispatchQueue.global(qos: .utility).async { let data = try? Data(contentsOf: self.configURL) DispatchQueue.main.async { self.parseConfig(data) } } } 3. 数据库操作 // 问题代码:主线程数据库查询 func loadUsers() { let users = database.query("SELECT * FROM users") // 阻塞 tableView.reloadData() } // 优化后:异步查询 func loadUsersAsync() { database.queryAsync("SELECT * FROM users") { [weak self] users in self?.users = users DispatchQueue.main.async { self?.tableView.reloadData() } } } 任务异步化 基本原则 flowchart TB subgraph main["主线程任务"] direction LR A1["UI更新视图刷新、动画"] A2["用户交互点击响应、手势处理"] A3["轻量计算简单数据转换"] end subgraph bg["后台线程任务"] direction LR B1["复杂计算数据处理、算法"] B2["I/O操作文件读写、网络请求"] B3["数据库操作查询、写入"] B4["图片处理解码、缩放、滤镜"] end main --> |"轻量、快速"| UI((用户界面)) bg --> |"耗时、阻塞"| main GCD任务调度 class AsyncTaskManager { // 计算密集型任务 static func compute<T>(_ work: @escaping () -> T, completion: @escaping (T) -> Void) { DispatchQueue.global(qos: .userInitiated).async { let result = work() DispatchQueue.main.async { completion(result) } } } // I/O任务 static func io<T>(_ work: @escaping () throws -> T, completion: @escaping (Result<T, Error>) -> Void) { DispatchQueue.global(qos: .utility).async { do { let result = try work() DispatchQueue.main.async { completion(.success(result)) } } catch { DispatchQueue.main.async { completion(.failure(error)) } } } } // 低优先级后台任务 static func background(_ work: @escaping () -> Void) { DispatchQueue.global(qos: .background).async { work() } } } // 使用示例 AsyncTaskManager.compute({ // 复杂计算 return self.processLargeDataSet() }) { result in // 主线程更新UI self.displayResult(result) } Swift Concurrency // 使用async/await class DataProcessor { func processAsync() async throws -> ProcessedData { // 在后台执行 return try await Task.detached(priority: .userInitiated) { return self.heavyProcessing() }.value } @MainActor func loadAndDisplay() async { do { let data = try await processAsync() // 自动在主线程更新UI updateUI(with: data) } catch { showError(error) } } } // 使用TaskGroup并行处理 func processImagesParallel(urls: [URL]) async -> [UIImage] { await withTaskGroup(of: UIImage?.self) { group in for url in urls { group.addTask { await self.loadImage(from: url) } } var images: [UIImage] = [] for await image in group { if let image = image { images.append(image) } } return images } } 任务拆分与调度 大任务拆分 当必须在主线程执行大量工作时,可以拆分成小块: ...

May 2, 2026

卡顿-原理

理解卡顿的原理是优化的基础。本文从屏幕显示的底层机制出发,沿着"VSync信号 -> 渲染管线 -> RunLoop协作 -> 掉帧产生"的逻辑链条,逐步揭示卡顿产生的根本原因。 屏幕显示的基础:VSync与缓冲机制 要理解卡顿,首先要理解屏幕是如何显示画面的。 VSync信号 VSync(Vertical Synchronization,垂直同步)是显示器发出的信号,用于协调GPU渲染和屏幕显示的时机。在60Hz的屏幕上,每16.67ms发出一次VSync信号。当信号到来时,屏幕从帧缓冲区读取数据进行显示。 时间轴: │←── 16.67ms ──→│←── 16.67ms ──→│←── 16.67ms ──→│ ↑ ↑ ↑ VSync 1 VSync 2 VSync 3 │ │ │ 显示帧1 显示帧2 显示帧3 双缓冲与三缓冲 为了避免画面撕裂(GPU写入和屏幕读取同一块内存导致),iOS使用多缓冲机制: flowchart LR subgraph 双缓冲机制 direction TB GPU["GPU"] -->|渲染到| BB["后缓冲区(Back Buffer)"] BB <-->|VSync时交换| FB["前缓冲区(Frame Buffer)"] FB -->|读取显示| Display["显示器"] end 双缓冲(默认):GPU渲染到后缓冲区,VSync到来时交换前后缓冲区,显示器从前缓冲区读取。 三缓冲(高负载时自动启用):增加第三个缓冲区,当渲染无法在一个VSync周期内完成时,GPU可以在额外缓冲区继续渲染,不必等待交换完成。代价是增加一帧延迟和内存占用。 系统在两者之间动态切换,开发者无需手动干预。 ProMotion自适应刷新率 ProMotion设备(iPhone 13 Pro及以上)支持10Hz-120Hz的自适应刷新率,这意味着帧时间预算不再固定: 刷新率 帧时间 卡顿阈值建议 120Hz 8.33ms >16ms视为掉帧 60Hz 16.67ms >33ms视为掉帧 30Hz 33.33ms >66ms视为掉帧 // 检查设备最大刷新率 let maxFrameRate = UIScreen.main.maximumFramesPerSecond // CADisplayLink适配ProMotion if #available(iOS 15.0, *) { displayLink?.preferredFrameRateRange = CAFrameRateRange( minimum: 60, maximum: 120, preferred: 120 ) } iOS渲染架构 了解了屏幕显示机制后,接下来看iOS是如何将UI元素最终变成屏幕上的像素的。 ...

May 2, 2026

卡顿-图片优化

图片处理是iOS应用中常见的性能瓶颈。本文介绍图片解码原理以及各种优化方案。 图片解码原理 为什么需要解码 图片文件(PNG、JPEG等)是压缩格式,无法直接显示。GPU需要的是位图(Bitmap)格式: flowchart LR A[压缩图片PNG/JPEG] --> B[解码DecodeCPU密集操作] B --> C[位图BitmapGPU可直接使用] 位图大小 = 宽度 × 高度 × 每像素字节数 例如:1000×1000 RGBA图片 = 1000 × 1000 × 4 = 4MB 默认解码时机 // 加载图片(此时未解码) let image = UIImage(named: "large_image") // 设置到ImageView(仍未解码) imageView.image = image // 在 CATransaction commit 的 prepare 阶段,Core Animation 会在主线程对未解码的图片执行解码 // 未提前解码的图片一定会在此阶段被解码,从而阻塞主线程引发卡顿 异步解码 基本原理 将解码工作移到后台线程: sequenceDiagram participant M as 主线程 participant B as 后台线程 M->>B: 请求加载图片 Note over M: 继续处理其他事件 B->>B: 加载压缩数据 B->>B: 解码为位图 B->>B: 创建CGImage B->>M: 返回解码后的图片 M->>M: 更新UI 实现方案1:强制解码 extension UIImage { /// 强制解码图片 func decodedImage() -> UIImage? { guard let cgImage = self.cgImage else { return nil } let width = cgImage.width let height = cgImage.height // 创建位图上下文 let colorSpace = CGColorSpaceCreateDeviceRGB() let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue) guard let context = CGContext( data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue ) else { return nil } // 绘制到上下文(触发解码) context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height)) // 从上下文创建新图片 guard let decodedCGImage = context.makeImage() else { return nil } return UIImage(cgImage: decodedCGImage, scale: scale, orientation: imageOrientation) } } // 异步使用 func loadImageAsync(named name: String, completion: @escaping (UIImage?) -> Void) { DispatchQueue.global(qos: .userInitiated).async { let image = UIImage(named: name)?.decodedImage() DispatchQueue.main.async { completion(image) } } } 实现方案2:使用ImageIO import ImageIO class ImageDecoder { static func decodeImage(from url: URL) -> UIImage? { guard let source = CGImageSourceCreateWithURL(url as CFURL, nil) else { return nil } let options: [CFString: Any] = [ kCGImageSourceShouldCache: true, kCGImageSourceShouldCacheImmediately: true // 立即解码 ] guard let cgImage = CGImageSourceCreateImageAtIndex(source, 0, options as CFDictionary) else { return nil } return UIImage(cgImage: cgImage) } static func decodeImageAsync(from url: URL, completion: @escaping (UIImage?) -> Void) { DispatchQueue.global(qos: .userInitiated).async { let image = ImageDecoder.decodeImage(from: url) DispatchQueue.main.async { completion(image) } } } } 实现方案3:使用UIGraphicsImageRenderer extension UIImage { func decodedImageUsingRenderer() -> UIImage { let format = UIGraphicsImageRendererFormat() format.scale = scale format.opaque = true format.preferredRange = .standard let renderer = UIGraphicsImageRenderer(size: size, format: format) return renderer.image { context in draw(at: .zero) } } } 图片降采样 为什么需要降采样 当图片尺寸远大于显示尺寸时,加载原图是浪费: ...

May 2, 2026

卡顿-检测

准确检测卡顿是优化的前提。本文介绍多种卡顿检测方案,从开发调试到线上监控都有对应的方案。 检测方案概览 方案 原理 优点 缺点 适用场景 CADisplayLink 监控帧节奏、FPS、慢帧/掉帧 简单直接、适合趋势观察 无法直接给出堆栈,FPS 只能辅助判断 开发调试/线上辅助指标 RunLoop Observer 监控RunLoop状态 可获取堆栈 有一定开销 开发/线上 子线程Ping 定时检测主线程响应 实现简单 精度有限 线上监控 Instruments 系统级分析 信息详细 只能开发时用 性能分析 MetricKit 系统数据收集 无额外开销 iOS 13+ 线上监控 Sentry ANR V2 帧延迟分析 区分阻塞类型 需集成SDK 线上监控 1. CADisplayLink监控 基本原理 CADisplayLink 是一个和屏幕显示刷新节奏同步的回调。它适合回答“当前 UI 刷新是否顺畅、是否出现慢帧、掉帧或冻结帧”,但 FPS 本身只是辅助参考,不能单独作为卡顿结论。 原因是: FPS 只描述结果,不描述原因:FPS 下降只能说明某段时间显示帧变少了,不能告诉你是主线程计算、布局、图片解码、锁等待、I/O、GPU 渲染还是系统主动降刷新率导致的。 ProMotion 会动态调整刷新率:在支持 120Hz 的机型上,系统可能根据内容和功耗策略把刷新率降到 10Hz、24Hz、30Hz、60Hz、80Hz、120Hz 等。静止页面低 FPS 可能只是系统主动省电,不是卡顿。 主线程阻塞会让回调延迟:如果主线程真的被阻塞,CADisplayLink 回调本身也会延后,此时需要结合实际回调间隔、RunLoop 状态和堆栈采样判断。 因此线上卡顿监控更推荐记录 帧间隔 / 慢帧 / 冻结帧 / 交互场景,而不是只显示一个“当前 FPS”。 ...

May 7, 2026

卡顿-离屏渲染

离屏渲染(Offscreen Rendering)是GPU渲染中的性能杀手。本文介绍离屏渲染的原理、触发条件以及优化方案。 什么是离屏渲染 正常渲染流程 正常情况下,GPU直接将渲染结果写入帧缓冲区(Frame Buffer): flowchart LR subgraph 正常渲染["正常渲染 On-Screen Rendering"] A[GPU渲染] --> B[Frame Buffer帧缓冲区] --> C[屏幕显示] end style A fill:#e1f5fe style B fill:#fff3e0 style C fill:#e8f5e9 特点: 直接渲染到帧缓冲区 渲染完成即可显示 效率最高 离屏渲染流程 离屏渲染需要先渲染到离屏缓冲区,再合成到帧缓冲区: flowchart LR subgraph 离屏渲染["离屏渲染 Offscreen Rendering"] A[GPU渲染] --> B[Offscreen Buffer离屏缓冲区] B --> C[Frame Buffer帧缓冲区] C --> D[屏幕显示] end E[额外的缓冲区] -.-> B style A fill:#e1f5fe style B fill:#f48fb1 style C fill:#fff3e0 style D fill:#e8f5e9 style E fill:#f48fb1,stroke:#f44336,stroke-dasharray: 5 5 性能开销: ...

May 2, 2026

卡顿

卡顿是影响用户体验的关键问题之一。当应用无法在一帧的时间内(通常为16.67ms)完成渲染工作时,就会出现掉帧,用户会感知到界面不流畅。 本系列文章从渲染原理入手,系统性地介绍iOS卡顿的检测与优化方法。 卡顿监控是 APM 流畅性子系的核心能力,线上指标体系、检测采集、上报策略请参考 APM 系列:指标体系、数据采集、业界方案(微信 Matrix / 字节 Slardar 卡死监控)。 什么是卡顿 iOS设备的屏幕刷新率通常为60Hz(ProMotion设备可达120Hz),这意味着每秒需要渲染60帧画面。每一帧的渲染时间窗口约为16.67ms(1000ms / 60)。 当CPU或GPU无法在这个时间窗口内完成工作时,就会发生掉帧(Frame Drop),用户会感知到卡顿。 掉帧情况 用户感知 严重程度 偶尔掉1-2帧 几乎无感知 轻微 连续掉帧 明显卡顿 中等 长时间阻塞(>250ms) 界面冻结 严重 渲染流程概述 iOS的渲染流程涉及CPU和GPU的协作,通过Core Animation Pipeline完成: flowchart TB subgraph APP["App 进程(CPU 阶段)"] direction TB Layout["Layout(布局计算)layoutSubviews、约束计算"] Display["Display(绘制显示)drawRect:、文本绘制"] Prepare["Prepare(准备)图片解码、格式转换"] Commit["Commit(提交)打包图层树"] Layout --> Display --> Prepare --> Commit end subgraph RS["Render Server 进程(GPU 阶段)"] direction TB Decode["解码图层树"] Vertex["顶点着色器"] Raster["光栅化"] Fragment["片段着色器"] Buffer["帧缓冲区写入"] Decode --> Vertex --> Raster --> Fragment --> Buffer end APP -->|"IPC 传输"| RS RS --> VSync["VSync 信号到来"] VSync --> Screen["显示到屏幕"] CPU与GPU的职责 阶段 主要工作 常见瓶颈 CPU 布局计算、视图创建、文本计算、图片解码 复杂布局、大量文本、主线程阻塞 GPU 纹理渲染、图层混合、离屏渲染 离屏渲染、大量透明图层、超大图片 卡顿的常见原因 CPU瓶颈 主线程阻塞:耗时操作在主线程执行 复杂布局:大量约束计算、频繁布局 文本计算:复杂富文本、大量文本渲染 图片解码:大图片在主线程解码 对象创建:大量对象的创建和销毁 GPU瓶颈 离屏渲染:圆角、阴影、遮罩触发离屏渲染 图层混合:大量透明图层叠加 超大图片:纹理尺寸超过GPU限制 复杂特效:模糊、滤镜等GPU密集操作 文章导航 本系列包含以下文章,建议按顺序阅读: ...

May 2, 2026

包瘦身-App Thinning

App Thinning是Apple提供的包体积优化技术,让用户只下载适合其设备的内容。本文详细介绍App Thinning的三个组成部分及其原理。 整体架构 开发者上传: ┌─────────────────────────────────────────┐ │ Universal IPA │ │ ├── arm64 代码 │ │ ├── @1x/@2x/@3x 图片 │ │ ├── iPhone/iPad 资源 │ │ └── Metal GPU资源(OpenGL ES已弃用) │ └─────────────────────────────────────────┘ ↓ App Store处理 ↓ ┌───────────┐ ┌──────────┐ ┌──────────┐ │ iPhone 12 │ │ iPhone 8 │ │ iPad Pro │ │ 变体 │ │ 变体 │ │ 变体 │ │ @3x图片 │ │ @2x图片 │ │ @2x图片 │ │ arm64 │ │ arm64 │ │ arm64 │ │ A14 GPU │ │ A11 GPU │ │ M1 GPU │ └───────────┘ └──────────┘ └──────────┘ App Thinning包含三个部分: ...

May 2, 2026

包瘦身-分析工具

在进行包体积优化之前,首先需要了解如何分析包体积。本文介绍常用的分析工具和Mach-O文件结构。 查看IPA构成 解压IPA文件分析各部分大小: # 解压IPA unzip -q YourApp.ipa -d ./ipa_content # 查看各文件大小 find ./ipa_content -type f -exec ls -lh {} \; | sort -k5 -h -r | head -20 LinkMap分析 LinkMap文件记录了链接器生成可执行文件的详细信息,可以精确分析二进制大小: # Build Settings中设置 Write Link Map File = YES Path to Link Map File = $(TARGET_TEMP_DIR)/$(PRODUCT_NAME)-LinkMap-$(CURRENT_VARIANT)-$(CURRENT_ARCH).txt LinkMap文件结构 # Path: /path/to/YourApp # Arch: arm64 # Object files: [ 0] linker synthesized [ 1] /path/to/ViewController.o [ 2] /path/to/Model.o # Sections: # Address Size Segment Section 0x100001000 0x00012345 __TEXT __text 0x100013345 0x00001234 __TEXT __stubs # Symbols: # Address Size File Name 0x100001000 0x00000100 [ 1] -[ViewController viewDidLoad] 0x100001100 0x00000050 [ 2] -[Model init] 解析脚本 可以使用脚本解析LinkMap统计各模块大小: ...

May 2, 2026

包瘦身-可执行文件优化

可执行文件通常占iOS应用包体积的30-50%,是优化的重点。本文介绍各种可执行文件优化手段及其原理。 编译器优化选项 优化级别原理 编译器优化级别控制了LLVM在编译时应用的优化Pass数量和类型。 什么是优化Pass? Pass是指编译器对代码进行的一次特定优化处理过程。LLVM将优化工作分解为多个独立的Pass,每个Pass负责一种特定类型的优化: 源代码 → 前端解析 → IR(中间表示)→ Pass1 → Pass2 → Pass3 → ... → 优化后的代码 → 机器码 常见的优化Pass包括: Pass名称 作用 Dead Code Elimination 删除永远不会执行的代码 Constant Folding 编译时计算常量表达式,如3+5直接变成8 Function Inlining 将函数调用替换为函数体本身 Loop Unrolling 展开循环,减少循环控制开销 Common Subexpression Elimination 消除重复计算的表达式 不同的优化级别启用不同数量和类型的Pass: # Build Settings Optimization Level = -Os # 级别:-Os 优化大小(推荐用于Release) -O0(无优化) 不应用任何优化Pass,编译速度最快。代码按原样生成,保留所有变量和控制流,便于调试。适用于Debug构建。 -O1(基础优化) 应用不显著增加编译时间的基础优化,包括: 基本的死代码消除 简单的常量折叠 基本块合并 死代码消除: 死代码消除(Dead Code Elimination)是指编译器自动移除永远不会被执行或执行结果不会被使用的代码。注意:这里的死代码消除是编译阶段的优化,作用于函数内部;而后文提到的Dead Code Stripping是链接阶段的优化,用于移除未被引用的整个函数或类。 -O1 适合需要一定优化但对编译速度敏感的场景。 -O2(标准优化) 应用大多数优化Pass,是性能和体积的平衡点。包括: ...

May 2, 2026