swift-08-属性、汇编分析inout本质
·
一、Swift中跟实例相关的属性可以分为2大类
1.1 存储属性( Stored Property)
类似于成员变量这个概念
存储在实例的内存中
结构体、类可以定义存储属性
枚举不可以定义存储属性(因为枚举只存储关联值和case)
枚举类型也可以定义类型属性(存储类型属性、计算类型属性 但是 枚举不可以定义存储属性(因为枚举只存储关联值和case)
原因:
核心原因在于枚举的本质是 “值类型” 且主要用于表示离散的 “状态 / 选项”,而非像类 / 结构体那样存储复杂的实例数据
一、枚举的属性规则:核心结论
-
允许定义的属性:
- 存储类型属性(
static let/var):属于枚举类型本身,所有枚举值共享,编译期需初始化。 - 计算类型属性(
static var { get }):基于其他值动态计算,无存储。 - 计算实例属性(
var { get }):基于枚举值 / 关联值动态计算,无存储。
- 存储类型属性(
-
禁止定义的属性:
- 存储实例属性:不能在枚举中定义
var xxx: Type这种需要分配内存存储的实例属性。
- 存储实例属性:不能在枚举中定义
二、为什么枚举不能有 “存储实例属性”?
根本原因是枚举的内存布局由其 “case 本身” 和 “关联值” 决定,不允许额外的实例存储:
- 枚举的每个
case本质是一个 “标识符”,若带关联值(如case point(x: Int, y: Int)),则关联值会作为该case的一部分存储。 - 若允许存储实例属性,会破坏枚举的 “值类型简洁性”,使其变得像结构体,违背了枚举 “表示离散状态” 的设计初衷。
三、替代方案:若需要 “类似存储实例属性” 的功能
如果希望枚举值携带额外数据,应优先使用关联值(枚举的原生特性),而非试图定义存储实例属性
1.2 计算属性( Computed Property)
本质就是方法(函数)
不占用实例的内存
枚举、结构体、类都可以定义计算属性
|
print(MemoryLayout<Circle>.stride) // 8 |
struct Circle {
// 存储属性
var radius: Double
// 计算属性
var diameter: Double {
set {
radius = newValue / 2
}
get {
radius * 2
}
}
}
var circle = Circle(radius: 5)
print(circle.radius) // 5.0
print(circle.diameter) // 10.0
circle.diameter = 12
print(circle.radius) // 6.0
print(circle.diameter) // 12.0

二、存储属性
在 Swift 中,存储属性是与实例关联的、用于存储值的属性
在创建类 或 结构体的实例时,必须为所有的存储属性设置一个合适的初始值
可以在初始化器里为存储属性设置一个初始值
可以分配一个默认的属性值作为属性定义的一部分
三、计算属性
如果这个属性可以通过宁一个属性计算出来就设计为计算属性

有set 必须有get

四、枚举rawValue原理
枚举原始值rawValue的本质是:只读计算属性
enum TestEnum : Int {
case test1 = 1, test2 = 2, test3 = 3
var rawValue: Int {
switch self {
case .test1:
return 10
case .test2:
return 11
case .test3:
return 12
}
}
}
print(TestEnum.test3.rawValue) // 12
五、延迟存储属性( Lazy Stored Property)

class PhotoView {
lazy var image: Image = {
let url = "https://www.520it.com/xx.png"
let data = Data(url: url)
return Image(data: data)
}() 调用闭包
}
六、延迟存储属性注意点

七、属性观察器(Property Observer )

八、全局变量、局部变量
九、inout的再次详细研究 1.22 输入输出参数本质就是引用传递



十、类型属性( Type Property)

十一、类型属性细节


十二、类(class)实例和值类型(结构体 / 枚举)实例属性修改相关
结论:
- 对于类实例而言,
let仅仅是限制了实例引用不能被改变,实例内部用var声明的属性依然可以修改。- 而对于结构体和枚举这些值类型的实例,
let会限制整个实例不能被修改,不管实例内部的属性是用let还是var声明的
1、类(class)实例的情况
要是你声明的是类的实例,那么即便是用 let 定义的存储属性,在实例初始化
之后也是可以修改的。不过,前提是这个属性本身是用 var 声明的
class Person {
let id: String // 这个存储属性必须在初始化时赋值,之后不能修改
var name: String // 这个存储属性可以在初始化之后修改
init(id: String, name: String) {
self.id = id
self.name = name
}
}
let person = Person(id: "123", name: "Alice")
person.name = "Bob" // 没问题,可以修改
// person.id = "456" // 会报错,因为 id 是用 let 声明的
2、值类型(结构体 / 枚举)实例的情况
当你声明的是结构体或者枚举的实例时,如果实例是用 let 定义的,
那么即使用 var 声明的存储属性,在实例初始化之后也不可以修改。
struct Point {
var x: Int
var y: Int
}
let point = Point(x: 0, y: 0)
// point.x = 10 // 会报错,因为 point 是用 let 定义的
var anotherPoint = Point(x: 0, y: 0)
anotherPoint.x = 10 // 没问题,因为 anotherPoint 是用 var 定义的
3、类 / 结构体的对比
十三、枚举类型定义类型属性(存储类型属性、计算类型属性) 和不可定义实例存储属性
- 存储类型属性(
static let/var):属于枚举类型本身,所有枚举值共享,编译期需初始化。 - 计算类型属性(
static var { get }):基于其他值动态计算,无存储。 - 计算实例属性(
var { get }):基于枚举值 / 关联值动态计算,无存储。
- 设计目的:枚举用于表示有限的一组值(如状态、选项),而非存储数据。
- 内存安全:如果枚举有存储属性,每个 case 的实例都需要为这些属性分配内存,但某些 case 可能不需要这些属性(例如
case success与case error(String))
1、计算类型属性(Computed Type Properties)
枚举可以定义计算类型属性,这类属性和计算实例属性类似,能够实现 get 和可选的 set 方法。下面是一个示例:
enum Device {
static var currentModel: String {
#if targetEnvironment(simulator)
return "Simulator"
#else
return "Actual Device"
#endif
}
}
print(Device.currentModel) // 会输出 "Simulator" 或者 "Actual Device"
2、存储类型属性(Stored Type Properties)
枚举定义存储类型属性时,需要遵循以下规则:
- 必须是静态的(static):由于枚举没有实例存储属性,所以存储类型属性必须用
static关键字修饰。- 需要初始值:必须在定义时就设置初始值,或者在静态初始化器里赋值。
- 全局唯一:存储类型属性属于枚举类型本身,在整个应用程序的生命周期中只有一份实例
enum AppSettings {
static var theme: String = "light" // 直接设置初始值
static var userCount = 0 // 初始值为 0
static let maxUsers = 100 // 常量存储类型属性
static func reset() {
theme = "light"
userCount = 0
}
}
// 使用存储类型属性
AppSettings.userCount += 1
print(AppSettings.userCount) // 输出: 1
3、枚举没有实例存储属性
枚举的主要目的是定义一组相关的值(如状态、类型等),而不是存储数据。因此,Swift 不允许为枚举定义实例存储属性
enum Direction {
// ❌ 错误:枚举不能有实例存储属性
// var degrees: Int = 0
case north, south, east, west
}
4、枚举的合法属性类型
- 关联值(Associated Values):为枚举的每个 case 附加数据(类似 “临时存储”)。
- 计算实例属性:基于枚举值计算结果。
- 静态存储 / 计算属性:属于枚举类型本身
enum Shape {
case circle(radius: Double) // 关联值作为临时数据
case rectangle(width: Double, height: Double)
// ✅ 计算实例属性
var area: Double {
switch self {
case .circle(let radius):
return Double.pi * radius * radius
case .rectangle(let width, let height):
return width * height
}
}
}
let circle = Shape.circle(radius: 5.0)
print(circle.area) // 78.53981633974483
5、枚举 vs. 结构体 / 类

更多推荐


所有评论(0)