@propertyWrapper属性包装器:在定义存储属性时添加一个分离层,代表该属性被包装起来,且在包装器内部可以做一些事情里面的主要几个实例:
- wrappedValue(初始化指定值和数据类型 - 必须实现);
- projectedValue(附加参数可以指定类型);
- 自动生成带下划线_的属性名称(指向当前属性包装器);
- 构造函数通常默认实现init(wrappedValue:),开发中通常进行自定义;
@propertyWrapper enum Lazy<Value> { case uninitialized(() -> Value) case initialized(Value) var wrappedValue: Value { mutating get { switch self { case .uninitialized(let initializer): let v = initializer() self = .initialized(v) return v case .initialized(let v): return v } } set { self = .initialized(newValue) } } var projectedValue: Self { get { self } set { self = newValue } } init(wrappedValue: @autoclosure @escaping () -> Value) { self = .uninitialized(wrappedValue) } func test() { print("call - test()") } } extension Lazy { init(body: @autoclosure @escaping () -> Value) { self = .uninitialized(body) } }
解释见注释
func testWrapped() { //属性包装器可以应用于全局、局部或类型范围内的属性。这些属性可以有观察访问器 ( willSet/ didSet),但不能有显式编写的 getter 或 setter。 @Lazy var height = 10 //等价于 var _tmp = Lazy<Int>(wrappedValue: 10) var tmp: Int { get { return _tmp.wrappedValue } set { _tmp.wrappedValue = newValue } } print("height = \(height)") //代表获取的是wrappedValue的值 print("_height = \(_height)") //Lazy对象 - 这里指的是枚举值 print("$height = \($height)") //projectedValue的值 - 类型可以自定义 $height.test() }
运行结果
height = 10 _height = initialized(10) $height = initialized(10) //这里是因为projectedValue的类型还是枚举 - 此处是可以自定义的 call - test()
构造方法
init(body: @autoclosure @escaping () -> Value) { self = .uninitialized(body) } init(wrappedValue: @autoclosure @escaping () -> Value, other: String) { self = .uninitialized(wrappedValue) print(other) }
调用
func testWrapped() { //可省略wrappedValue:参数 @Lazy(other: "other") var height = 10 //等价 @Lazy(wrappedValue: 20, other: "other") var age //调用其他构造方法 @Lazy(body: 30) var weight }
保存数据
@propertyWrapper struct SaveValue<Value> { let key: String let `default`: Value var projectedValue: String = "测试projectedValue - 这里可以指定任意类型做辅助判断" var wrappedValue: Value { get { return UserDefaults.standard.object(forKey: key) as? Value ?? self.default } set { UserDefaults.standard.set(newValue, forKey: key) } } init(key: String, `default`: Value) { self.key = key self.default = `default` } } func testWrapped() { @SaveValue(key: "saveKey", default: "默认值") var name: String print(name) print(_name) print($name) name = "李四" print(name) }
运行结果
默认值 SaveValue<String>(key: "saveKey", default: "默认值", projectedValue: "测试projectedValue - 这里可以指定任意类型做辅助判断") 测试projectedValue - 这里可以指定任意类型做辅助判断 李四
再次运行 - 值已保存,没有再取默认值
李四 SaveValue<String>(key: "saveKey", default: "默认值", projectedValue: "测试projectedValue - 这里可以指定任意类型做辅助判断") 测试projectedValue - 这里可以指定任意类型做辅助判断 李四
加锁
@propertyWrapper class Atomic<Value> { private var lock: NSRecursiveLock = NSRecursiveLock() private var value: Value var wrappedValue: Value { get { lock.lock(); defer { lock.unlock() } return value } set { lock.lock(); defer { lock.unlock() } value = newValue } } init(wrappedValue value: Value) { self.value = value } }
使用限制
- 不能在协议内声明带有包装器的属性;
- 不能在扩展中声明带有包装器的实例属性;
- 实例属性不能在enum中使用;
- 在类中声明的具有包装器的属性不能覆盖另一个属性;
- 带有包装器的属性不能是lazy、@NSCopying 或@NSManagedweakunowned;
- 具有包装器的属性必须是在其封闭声明中声明的唯一属性;
- 带有包装器的属性不应定义 getter 或 setter;
- 属性包装器类型的属性wrappedValue和(如果存在)init(wrappedValue:)应具有与属性包装器类型相同的访问权限;
- 该projectedValue属性(如果存在)应具有与属性包装器类型相同的访问权限;
- 初始化init()器(如果存在)应具有与属性包装器类型相同的访问权限;