做iOS开发的朋友都知道,Swift写起来顺手,但一不小心就会踩到性能坑。尤其是当应用功能越来越多,页面越来越复杂的时候,卡顿、内存暴涨这些问题就容易冒出来。别急,这里分享几个在日常开发中特别管用的优化方法。
减少不必要的属性观察器
很多人喜欢在属性后面加 didSet,觉得方便监听变化。但如果这个属性频繁更新,比如在列表滚动时不断赋值,那每次都会触发 didSet 里的逻辑,拖慢主线程。可以加个判断,只有真正变化时才执行:
var items: [String] = [] {
didSet {
if items.count != oldValue.count {
updateUI()
}
}
}
懒加载不是万能钥匙
懒加载(lazy)确实能延迟对象创建,但滥用也会带来问题。比如在ViewController里把一堆视图都声明成lazy,看似节省了启动时间,可实际上这些资源最终还是得加载,反而让首次使用时卡一下。更适合的做法是:只对真正“非必需立即初始化”的对象用lazy,比如弹窗控制器或复杂计算模块。
避免隐式强引用循环
Closure里捕获self太常见了,一个不小心就内存泄漏。比如网络请求回调:
network.request { [weak self] result in
self?.handle(result)
}
加上 [weak self],防止self被closure持有导致无法释放。这个习惯得养成,特别是在tableView或UICollectionView的数据源处理中。
用Struct替代Class的场景
不是所有数据模型都要用class。如果只是存数据、不涉及继承和多态,用struct更轻量,还能享受值类型带来的线程安全优势。比如解析JSON返回的用户信息:
struct UserInfo {
let name: String
let age: Int
}
比class少了内存分配和引用管理的开销,尤其在高频创建销毁的场景下,性能提升明显。
图片加载别堵住主线程
从网络下载图片后直接赋给UIImageView?小心滑动列表时掉帧。应该先异步解码:
DispatchQueue.global(qos: .userInitiated).async {
if let image = UIImage(data: data),
let cgImage = image.cgImage {
let decodedImage = UIImage(cgImage: cgImage, scale: image.scale, orientation: .up)
DispatchQueue.main.async {
imageView.image = decodedImage
}
}
}
这样不会阻塞UI,用户体验更流畅。
预估Cell高度减少滚动卡顿
UITableView滚动不顺,很多时候是因为cell高度没预设好。自动计算要走layout流程,耗时大。如果内容结构相对固定,比如聊天记录每条高度差不多,可以直接设置 rowHeight:
tableView.rowHeight = 60 // 固定高度
// 或者用 estimatedRowHeight 减少初次加载压力
tableView.estimatedRowHeight = 60
哪怕估算不准,系统后续会调整,也比完全靠runtime算快得多。