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()器(如果存在)应具有与属性包装器类型相同的访问权限;
相关文章
|
2月前
|
存储 Swift
Swift开发——属性检查器
Swift中的属性检查器(willSet, didSet)允许在设置存储属性值前后执行代码。在类`Circle`中,属性`radius`使用属性观察器:willSet在赋值前检查值,若值为负则打印警告;didSet在赋值后比较新旧值,根据变化输出相应信息。在实例`c`中,`radius`从-5变为0时,输出“Input value is negative.”和“The circle gets smaller.”;从0变为10时,输出“Input value is normal.”和“The circle gets larger.”。
188 4
Swift开发——属性检查器
|
2月前
|
存储 程序员 Swift
Swift开发——存储属性与计算属性
Swift推荐使用结构体进行开发,结构体支持属性和方法,且作为值类型。结构体属性包括存储属性(如radius)和计算属性(如r),计算属性不存储值,类似方法。结构体用`struct`定义,命名遵循大驼峰规则。实例名遵循小驼峰规则。属性可在结构体中任意位置定义,静态属性用`static`。存储属性可为`lazy`实现懒加载。结构体实例通过`.`访问属性和方法,静态属性和方法用`结构体名.`访问。计算属性可读写,可通过`get`和`set`定义。程序段1展示了结构体Point和Circle的属性和方法,包括私有属性、只读计算属性、可读写计算属性及`mutating`方法。
20 0
Swift开发——存储属性与计算属性
|
3月前
|
安全 Swift iOS开发
【Swift开发专栏】Swift中的属性观察者与KVO
【4月更文挑战第30天】Swift编程语言支持属性观察者(`willSet`和`didSet`)和键值观察(KVO)来响应属性变化。属性观察者在设置前(`willSet`)和设置后(`didSet`)执行代码,可用于数据绑定。KVO是Cocoa/Cocoa Touch中的机制,需`NSObject`子类和`@objc dynamic`属性配合使用。注意在观察者销毁前移除观察,以避免内存问题。示例展示了属性观察者实现简单数据绑定。
51 1
|
3月前
|
存储 算法 安全
【Swift专题】聊聊Swift中的属性
属性是面向对象语言中非常基础的语法特性,我们讲属性,实际上就是讲与类本身或类实例关联的数据。在面向对象的语言中,类作为重要的数据结构会封装数据与函数,类中的函数我们通常称其为方法,而数据则就是属性。
71 1
|
存储 监控 Swift
Swift实用小册09:存储属性、计算属性及属性观察器的使用
Swift实用小册09:存储属性、计算属性及属性观察器的使用
243 0
Swift实用小册09:存储属性、计算属性及属性观察器的使用
|
存储 编译器 Swift
Swift5.0 - day4-闭包、属性、方法、下标
Swift5.0 - day4-闭包、属性、方法、下标
309 0
Swift5.0 - day4-闭包、属性、方法、下标
|
存储 Swift iOS开发
Swift5.1—类型属性
Swift5.1—类型属性
130 0
Swift5.1—类型属性
|
存储
Swift5.1——计算属性
Swift5.1——计算属性
150 0
Swift5.1——计算属性
|
存储 安全 编译器
Swift-进阶 02:类、对象、属性
Swift-进阶 02:类、对象、属性
308 0
Swift-进阶 02:类、对象、属性
Swift5.1—隐式解包可选值属性
Swift5.1—隐式解包可选值属性
235 0

相关课程

更多