Swift 计算属性(Computed Property)详解:原理、性能与实战

原文:What is a Computed Property in Swift? -- SwiftLee

什么是计算属性?

Swift 中的属性分为两大族谱:

类型 描述 存储值
Stored Property(存储属性) 保存一个固定的值,最常见 ✅
Computed Property(计算属性) 每次被访问时实时计算出一个值,不占用额外存储 ❌

计算属性的核心特征:"算完即走,不落痕迹"。

它通过 getter(必含)和可选的 setter 来间接读取或修改其他属性。

只读计算属性:最常见形态

典型场景:基于已有属性生成新值

swift 复制代码
struct Content {
    var name: String
    let fileExtension: String

    // 计算属性:拼接文件名
    var filename: String {
        name + "." + fileExtension   // 单行可省略 return
    }
}

let content = Content(name: "swiftlee-banner", fileExtension: "png")
print(content.filename)  // swiftlee-banner.png
  • filename 是 只读 的,无法赋值:content.filename = "new.png" 会编译错误。
  • 若显式写 get,代码更冗余,不推荐:
swift 复制代码
var filename: String {
    get { name + "." + fileExtension }
}

可读可写计算属性:暴露私有模型的接口

有时我们想把复杂的模型隐藏在内部,只暴露一个"代理"属性供外部读写------计算属性就能优雅完成。

swift 复制代码
struct ContentViewModel {
    private var content: Content   // 真正的数据模型对外不可见

    init(_ content: Content) {
        self.content = content
    }

    // 计算属性:既读又写,内部转发到 content.name
    var name: String {
        get { content.name }
        set { content.name = newValue }   // newValue 是 Swift 的默认形参
    }
}

var content = Content(name: "swiftlee-banner", fileExtension: "png")
var viewModel = ContentViewModel(content)
viewModel.name = "SwiftLee Post"
print(viewModel.name)  // SwiftLee Post

效果:调用者只知道 name,却不知道内部还有一个复杂的 Content 对象。

在 Extension 中使用计算属性:无痛加功能

计算属性可以写在 extension 里,为现有类型(尤其是系统类型)增加无痛扩展。

swift 复制代码
import UIKit

extension UIView {
    // 快速访问 frame 尺寸
    var width: CGFloat {
        frame.size.width
    }

    var height: CGFloat {
        frame.size.height
    }
}

let view = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 480))
print(view.width)  // 320

优点:无需继承,即刻生效。

在子类中重写计算属性

计算属性还可以 被 override,常用于定制 UIKit 行为。

最简单:直接硬编码

swift 复制代码
final class HomeViewController: UIViewController {
    override var prefersStatusBarHidden: Bool { true }
}

进阶:由内部存储属性驱动

swift 复制代码
final class HomeViewController: UIViewController {
    private var shouldHideStatusBar: Bool = true {
        didSet {
            // 状态改变后刷新系统样式
            setNeedsStatusBarAppearanceUpdate()
        }
    }

    override var prefersStatusBarHidden: Bool { shouldHideStatusBar }
}

何时使用计算属性?官方推荐 3 个条件

条件 示例场景
值依赖其他属性 上文的 filename
在 extension 中定义 给 UIView加 width/height
作为内部对象的受控访问点 ContentViewModel.name

个人补充:若计算逻辑 纯静态、且 无状态依赖,考虑直接声明为 static let,避免每次调用重新计算。

性能陷阱:每次访问都会重新计算

计算属性 不会缓存结果,高频访问 + 重计算 = 性能灾难。

反面教材:每次都排序

swift 复制代码
struct PeopleViewModel {
    let people: [Person]

    var oldest: Person? {
        people.sorted { $0.age > $1.age }.first   // O(n log n) 每次都要跑
    }
}

优化:移入初始化器,只算一次

swift 复制代码
struct PeopleViewModel {
    let people: [Person]
    let oldest: Person?

    init(people: [Person]) {
        self.people = people
        oldest = people.max(by: { $0.age < $1.age })   // 或者自己实现一次遍历找最大值
    }
}

经验法则:

  • 数据量小 or 变化频繁 → 计算属性
  • 数据量大 or 代价高昂 → 存储属性 + 预计算

计算属性 VS 方法:如何抉择?

维度 计算属性 方法 (func)
参数 无 可接受参数
可读性暗示 "轻量级值" "可能耗时"
测试/模拟 不方便 mock 容易 stub/mock
适用场景 简单、无参数、依赖内部状态 复杂、需参数、可能异步或耗时

一句话:重逻辑用方法,轻数据用属性。

总结 & 扩展场景

核心结论

  1. 计算属性 = 无存储 + 实时计算 + 可选 setter。
  2. 带来 语义化 API 与 封装性,但 不缓存。
  3. 在 extension、子类 override、MVVM 视图模型中大放异彩。

扩展实战场景

场景 代码示例 & 注释
格式化输出 var displayPrice: String { "(price)$" }
链式依赖 var isAdult: Bool { age >= 18 }→ var canDrink: Bool { isAdult }
Core Data 轻量级封装 在 NSManagedObject 的 extension 中,把 primitiveValue包装成计算属性,隐藏 KVC 细节
SwiftUI 绑定 在 ObservableObject 中,用计算属性把 @Published的私有变量暴露为 public 接口
缓存友好型计算属性 结合 lazy或自定义缓存字典,实现 "第一次算,之后读" 的懒加载计算属性
相关推荐
茶底世界之下6 小时前
Metal 冷启动并发下,Pipeline Cache 为什么必须合并在途编译
ios·swift
黑科技iOS上架1 天前
双引擎解决oc/swift开发引用加固难题
开发语言·ios·swift·审核·ios混淆·执行程序差异化
传奇开心果编程3 天前
【SwiftUI提高练中学】第13课 实时活动与灵动岛:ActivityKit 实战
学习·ui·ios·swiftui·swift
AC赳赳老秦3 天前
数据采集全链路审计留痕:用 OpenClaw 实现合规审计与追溯
开发语言·汇编·python·php·swift·deepseek·openclaw
传奇开心果编程3 天前
【SwiftUI入门练中学】第17课 通知与后台任务
学习·ui·ios·swiftui·swift
逐流人4 天前
Ceph对象存储与文件系统存储管理:从RADOS网关、S3与Swift到CephFS快照同步
linux·运维·ceph·云原生·云计算·swift·rados
传奇开心果编程5 天前
【SwiftUI入门练中学】第7课 动画与手势:让界面更灵动
学习·macos·ui·ios·swiftui·swift
传奇开心果编程6 天前
【SwiftUI娓娓道来】第3课:让界面活起来——交互与动画指南
学习·macos·ui·ios·swiftui·swift
用户38034165882976 天前
控制中心一键后台录音:我被自己的 Spike 骗了两次
ios·swift