Swift属性包装器@propertyWrapper

简介: Swift属性包装器@propertyWrapper

@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()器(如果存在)应具有与属性包装器类型相同的访问权限;
相关文章
|
3月前
|
存储 算法 安全
【Swift专题】聊聊Swift中的属性
属性是面向对象语言中非常基础的语法特性,我们讲属性,实际上就是讲与类本身或类实例关联的数据。在面向对象的语言中,类作为重要的数据结构会封装数据与函数,类中的函数我们通常称其为方法,而数据则就是属性。
41 1
|
存储 监控 Swift
Swift实用小册09:存储属性、计算属性及属性观察器的使用
Swift实用小册09:存储属性、计算属性及属性观察器的使用
218 0
Swift实用小册09:存储属性、计算属性及属性观察器的使用
|
存储 编译器 Swift
Swift5.0 - day4-闭包、属性、方法、下标
Swift5.0 - day4-闭包、属性、方法、下标
289 0
Swift5.0 - day4-闭包、属性、方法、下标
Swift5.1—隐式解包可选值属性
Swift5.1—隐式解包可选值属性
214 0
|
存储
Swift5.1—延迟属性
Swift5.1—延迟属性
361 0
|
存储 监控
Swift5.1—属性观察器
Swift5.1—属性观察器
196 0
|
存储 Swift iOS开发
Swift5.1—类型属性
Swift5.1—类型属性
115 0
Swift5.1—类型属性
|
存储
Swift5.1——计算属性
Swift5.1——计算属性
137 0
Swift5.1——计算属性
|
存储 监控 Swift
Swift5.1—属性
Swift5.1—属性
109 0
|
存储 安全 编译器
Swift-进阶 02:类、对象、属性
Swift-进阶 02:类、对象、属性
289 0
Swift-进阶 02:类、对象、属性

相关课程

更多