performance-optimization
SKILL.md
性能优化最佳实践
本技能提供 iOS 应用性能优化的标准工作流程和最佳实践。
核心原则
- 测量优先 - 先 profiling 再优化
- 关键路径 - 优先优化用户体验最敏感的部分
- 空间换时间 - 合理使用缓存
- 懒加载 - 按需加载资源
- 批量处理 - 减少 UI 更新频率
启动优化
启动流程分析
┌─────────────────────────────────────────────────────┐
│ 冷启动流程: │
│ 1. dyld 加载 → 2. Rebase/Bind → 3. ObjC 初始化 │
│ 4. +load 方法 → 5. C++ 静态初始化 → 6. AppDelegate │
│ 7. UIWindow 创建 → 8. RootViewController → 9. 首屏渲染 │
└─────────────────────────────────────────────────────┘
优化方案
// ✅ 正确:延迟非关键初始化
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 关键路径:尽快显示首屏
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = RootViewController()
window?.makeKeyAndVisible()
// 非关键:异步初始化
DispatchQueue.main.async {
self.setupNonCriticalServices()
}
return true
}
private func setupNonCriticalServices() {
// 延迟初始化非关键服务
AnalyticsManager.setup()
CrashReporter.setup()
FeedbackManager.setup()
}
}
减少 dyld 加载时间
// ✅ 正确:减少 +load 方法使用
// 在 Category/Base 类中避免使用 +load,改用 +initialize
// ❌ 错误:在 +load 中做耗时操作
+ (void)load {
[self expensiveSetup]; // 阻塞启动
}
// ✅ 正确:使用 dispatch_once 延迟初始化
+ (void)initialize {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self setup];
});
}
图片加载优化
// ✅ 正确:使用渐进式加载
class ImageView: UIView {
func loadImage(url: URL) {
// 1. 先显示低分辨率缩略图
thumbnailImageView.image = placeholder
// 2. 异步加载原图
DispatchQueue.global(qos: .userInitiated).async {
let data = try? Data(contentsOf: url)
let image = UIImage(data: data!)
// 3. 主线程更新
DispatchQueue.main.async {
UIView.transition(with: self,
duration: 0.2,
options: .transitionCrossDissolve,
animations: {
self.imageView.image = image
})
}
}
}
}
内存优化
图片内存优化
// ✅ 正确:图片解码在后台
func loadImage() {
DispatchQueue.global(qos: .userInitiated).async {
// 预解码图片(避免在主线程解码)
let image = UIImage(contentsOfFile: path)!
_ = image.cgImage?.dataProvider?.data
DispatchQueue.main.async {
imageView.image = image
}
}
}
// ✅ 正确:使用 Image Cache
let imageCache = NSCache<NSString, UIImage>()
imageCache.countLimit = 100
imageCache.totalCostLimit = 1024 * 1024 * 50 // 50MB
// ✅ 正确:大图使用 downsampling
func downsample(imageAt imageURL: URL, to pointSize: CGSize, scale: CGFloat) -> UIImage {
let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
guard let imageSource = CGImageSourceCreateWithURL(imageURL as CFURL, imageSourceOptions) else {
return UIImage()
}
let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scale
let downsampleOptions = [
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceShouldCacheImmediately: true,
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels
] as CFDictionary
guard let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions) else {
return UIImage()
}
return UIImage(cgImage: downsampledImage)
}
减少内存峰值
// ✅ 正确:使用 autorelease pool 处理大量临时对象
func processLargeData() {
for chunk in dataChunks {
autoreleasepool {
// 临时对象在 pool 结束后释放
let processed = process(chunk)
save(processed)
}
}
}
// ✅ 正确:延迟加载大对象
class DataViewController: UIViewController {
private lazy var largeData: Data = {
// 只在需要时加载
return loadLargeData()
}()
private var _largeData: Data?
func loadLargeData() -> Data {
// 加载逻辑
}
}
检测内存泄漏
// ✅ 正确:使用 weak 检测循环引用
class ViewController: UIViewController {
deinit {
// 如果没打印,说明有循环引用
print("\(type(of: self)) deinitialized")
}
}
// Instruments 检测:
// 1. Allocations - 内存分配
// 2.Leaks - 内存泄漏
// 3. VM Tracker - 虚拟内存
渲染性能
保持 60 FPS
// ✅ 正确:避免在主线程做耗时操作
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
// ❌ 错误:主线程图片解码
// cell.imageView?.image = loadImage(from: url)
// ✅ 正确:异步图片加载
cell.imageView?.image = placeholder
loadImageAsync(url: url) { image in
cell.imageView?.image = image
}
return cell
}
减少离屏渲染
// ✅ 正确:使用 CAShapeLayer 代替圆角
class RoundedView: UIView {
private let borderLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
setupLayer()
}
private func setupLayer() {
borderLayer.path = UIBezierPath(roundedRect: bounds,
cornerRadius: 12).cgPath
borderLayer.fillColor = UIColor.clear.cgColor
borderLayer.strokeColor = UIColor.black.cgColor
borderLayer.lineWidth = 2
layer.addSublayer(borderLayer)
}
// ❌ 避免:离屏渲染
// layer.cornerRadius = 12
// layer.masksToBounds = true
}
批量更新
// ✅ 正确:批量插入减少动画次数
func insertItems() {
tableView.beginUpdates()
tableView.insertRows(at: indexPaths, with: .automatic)
tableView.endUpdates()
}
// ✅ 正确:禁用动画提升性能
UIView.performWithoutAnimation {
// 批量更新代码
self.tableView.reloadData()
self.layoutIfNeeded()
}
网络优化
请求合并
// ✅ 正确:合并多个请求
func loadUserData() async throws -> UserData {
// ❌ 错误:串行请求
// let user = try await fetchUser()
// let posts = try await fetchPosts(userId: user.id)
// let stats = try await fetchStats(userId: user.id)
// ✅ 正确:并行请求
async let user = fetchUser()
async let posts = fetchPosts(userId: user.id)
async let stats = fetchStats(userId: user.id)
return try await UserData(user: user, posts: posts, stats: stats)
}
缓存策略
// ✅ 正确:使用 URLCache
let cache = URLCache(memoryCapacity: 50_000_000, diskCapacity: 100_000_000)
URLCache.shared = cache
let session = URLSession(configuration: {
let config = URLSessionConfiguration.default
config.requestCachePolicy = .returnCacheDataElseLoad
config.urlCache = cache
return config
}())
// ✅ 正确:ETag 验证
func fetchData(withETag etag: String?) async throws -> (Data, String) {
var request = URLRequest(url: apiURL)
if let etag = etag {
request.setValue(etag, forHTTPHeaderField: "If-None-Match")
}
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw NetworkError.invalidResponse
}
if httpResponse.statusCode == 304 {
// 使用缓存
return (cachedData, cachedETag)
}
let newETag = httpResponse.allHeaderFields["ETag"] as? String
return (data, newETag ?? "")
}
电量优化
减少 CPU 使用
// ✅ 正确:停止不必要的定时器
class ViewController: UIViewController {
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
startTimer()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
stopTimer() // 页面不可见时停止
}
private func stopTimer() {
timer?.invalidate()
timer = nil
}
}
位置服务优化
// ✅ 正确:按需请求位置
class LocationManager: NSObject {
func startUpdatingLocation(accuracy: LocationAccuracy) {
switch accuracy {
case .navigation:
// 高精度,耗电
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = 10
case .visit:
// 低精度,省电
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager.distanceFilter = 100
}
locationManager.startUpdatingLocation()
}
func stopUpdatingLocation() {
locationManager.stopUpdatingLocation()
}
}
Instruments 使用
性能分析流程
1. Time Profiler - 找到 CPU 瓶颈
2. Allocations - 分析内存分配
3. Leaks - 检测内存泄漏
4. Energy Log - 电量消耗分析
5. Network - 网络请求分析
关键指标
| 指标 | 目标值 | 工具 |
|---|---|---|
| 启动时间 | < 1s | os_signpost |
| FPS | 60 (不低于 45) | Core Animation |
| 内存峰值 | < 设备限制 50% | Allocations |
| CPU 使用 | < 20% | Activity Monitor |
| 网络延迟 | < 500ms | Network Link Conditioner |
检查清单
在发布前,请确认:
- 启动时间已优化(冷启动 < 1s)
- 无明显卡顿(FPS 稳定 60)
- 内存泄漏已修复
- 图片已压缩和缓存
- 网络请求已优化(缓存、合并)
- 后台任务已正确管理
- 电量消耗已测试
参考资源
Weekly Installs
3
Repository
creeveliu/ios-skillsGitHub Stars
2
First Seen
10 days ago
Security Audits
Installed on
amp3
cline3
opencode3
cursor3
kimi-cli3
codex3