Swift 中的属性包装器是什么?如何使用 @propertyWrapper?
Swift 中的属性包装器是一种在属性设置和获取时添加额外逻辑的机制。属性包装器可以复用属性管理逻辑,减少代码重复。
基本属性包装器:
swift@propertyWrapper struct TwelveOrLess { private var number: Int init() { self.number = 0 } var wrappedValue: Int { get { return number } set { number = min(newValue, 12) } } } struct SmallRectangle { @TwelveOrLess var height: Int @TwelveOrLess var width: Int } var rectangle = SmallRectangle() rectangle.height = 10 rectangle.width = 20 print(rectangle.width) // 12
带参数的属性包装器:
swift@propertyWrapper struct SmallNumber { private var maximum: Int private var number: Int init(wrappedValue: Int, maximum: Int) { self.maximum = maximum self.number = wrappedValue } var wrappedValue: Int { get { return number } set { number = min(newValue, maximum) } } } struct ZeroRectangle { @SmallNumber(wrappedValue: 0, maximum: 5) var height: Int @SmallNumber(wrappedValue: 0, maximum: 10) var width: Int }
投射值:
swift@propertyWrapper struct SmallNumber { private var number: Int init(wrappedValue: Int) { self.number = wrappedValue } var wrappedValue: Int { get { return number } set { number = min(newValue, 12) } } var projectedValue: Bool { return number > 12 } } struct SomeStructure { @SmallNumber var someNumber: Int } var someStructure = SomeStructure() someStructure.someNumber = 4 print(someStructure.$someNumber) // false someStructure.someNumber = 14 print(someStructure.$someNumber) // true
常见的属性包装器用例:
-
线程安全:
swift@propertyWrapper struct ThreadSafe { private var value: T private let lock = NSLock() init(wrappedValue: T) { self.value = wrappedValue } var wrappedValue: T { get { lock.lock() defer { lock.unlock() } return value } set { lock.lock() defer { lock.unlock() } value = newValue } } } -
延迟加载:
swift@propertyWrapper struct Lazy { private var value: T? var wrappedValue: T { mutating get { if let value = value { return value } let initialValue = wrappedValueFactory() value = initialValue return initialValue } } let wrappedValueFactory: () -> T init(wrappedValue: @autoclosure @escaping () -> T) { self.wrappedValueFactory = wrappedValue } } -
用户默认值:
swift@propertyWrapper struct UserDefault { let key: String let defaultValue: T var wrappedValue: T { get { UserDefaults.standard.object(forKey: key) as? T ?? defaultValue } set { UserDefaults.standard.set(newValue, forKey: key) } } }
属性包装器的最佳实践:
- 使用属性包装器封装重复的属性逻辑
- 使用投射值提供额外的功能
- 为属性包装器提供合理的默认值
- 注意属性包装器的性能影响
- 在适当的地方使用属性包装器