iOS 属性包装器propertyWrapper

3,319 阅读3分钟

定义

在 Swift 5.1 中引入了@propertyWrapper这个特性,它可以为属性添加一些自定义的行为,从而使代码更加简洁、可读、易于维护。@propertyWrapper是一个属性包装器,可以使用它为属性添加一些自定义的行为。一个属性包装器是一个结构体,用于管理属性的存储和访问。它可以定义自己的getset方法,并在属性读写时触发自己的逻辑。

作用

@propertyWrapper可以提供以下几个作用:

  • 简化代码:使用@propertyWrapper可以简化代码,减少重复的逻辑
  •  增加可读性:使用@propertyWrapper可以使代码更加易于阅读和理解,因为它可以提供一些自定义的行为和描述
  • 优化性能:使用@propertyWrapper可以优化代码的性能,因为它可以控制属性的存储和访问方式
  • 提高安全性:使用@propertyWrapper可以提高代码的安全性,因为它可以验证属性值的合法性,避免错误的输入

使用场景

@propertyWrapper可以用于以下场景:

  • 验证属性值的合法性,例如范围、格式、唯一性等
  • 处理属性值的变化,例如映射、转换、格式化等
  • 控制属性的存储和访问方式,例如延迟加载、懒加载、缓存等
  • 实现属性的依赖关系,例如自动更新、自动绑定等

添加验证规则

@propertyWrapper
struct NonEmptyString {
    private var value: String = ""

    init(wrappedValue: String) {
        if wrappedValue.isEmpty {
            self.value = "Unnamed"
        } else {
            self.value = wrappedValue
        }
    }

    var wrappedValue: String {
        get { value }
        set {
            if !newValue.isEmpty {
                value = newValue
            }
        }
    }
}

struct Person {
    @NonEmptyString var name: String
}

var person1 = Person(name: "") // name will be set to "Unnamed"
var person2 = Person(name: "John") // name will be set to "John"​

上面的例子中,我们使用 NonEmptyString 属性包装器来对 Person 实例的 name 属性进行验证,这使得代码更加健壮和可靠,避免了一些错误的输入数据

添加缓存功能

@propertyWrapper
class Cached<T> {
    private var value: T
    private var cachedValue: T?
    init(wrappedValue: T) {
        self.value = wrappedValue
    }

    var wrappedValue: T {
        get {
            if let cachedValue = cachedValue {
                return cachedValue
            } else {
                cachedValue = value
                return value
            }
        }
        set {
            value = newValue
            cachedValue = nil
        }
    }
}

class NetworkClient {
    @Cached var data: [String] = []
    func fetchData() { /* ... */ }
} 

var client = NetworkClient()
client.data = ["a", "b", "c"]
print(client.data) // ["a", "b", "c"]

实现自动更新

后续更新

注意事项

  • @propertyWrapper 只能用于属性,不能用于计算属性或者函数
  • @propertyWrapper 可以应用于单个属性或者整个结构体/类中的所有属性
  • 使用 @propertyWrapper 时,需要为包装器提供一个默认值
  • 如果属性包装器需要进行一些额外的初始化,需要使用 init(wrappedValue:) 方法
  • 使用 @propertyWrapper 时,需要为包装器添加 wrappedValue 属性
  • 使用 @propertyWrapper时,需要遵循 Swift 中的命名规范,例如使用驼峰命名法

总结

@propertyWrapper 是 Swift 中非常强大的特性之一,可以用于实现许多常见的功能,例如属性验证、缓存、自动更新等。使用 @propertyWrapper 可以使代码更加简洁、可读、易于维护,因为它将属性包装器和属性本身分离开来,并将某些常见的行为抽象出来