Swift中的 @property Wrapper

属性包装器

属性包装器在管理属性如何存储和定义属性的代码之间添加了一个分隔层。举例来说,如果你的属性需要线程安全性检查或者需要在数据库中存储它们的基本数据,那么必须给每个属性添加同样的逻辑代码。当使用属性包装器时,你只需在定义属性包装器时编写一次管理代码,然后应用到多个属性上来进行复用。
比如定义一个包装器TestA,TestA确保其值在大于10的时候返回10,小于10的时候就返回对应的值。
在Test A这个例子中,我们还定义了一个构造方法,给num赋初始值。在使用的时候,我们可以通过@TestA(numValue: 5) var width: Int 这种方式给对应的装饰器赋值,这句话相当于width的初始值为5。
我们还可以通过$符号来获取装饰器的一个呈现值。在TestA中projectedValue就是呈现值,通过range1.$width来访问。

@propertyWrapper
struct TestA {
    var num: Int
    var projectedValue: Bool
    var wrappedValue: Int {
        get {
           return num
        }
        set {
            if newValue > 10 {
                num = 10
                projectedValue = true
            }else {
                num = newValue
            }
        }
    }
    
    init(numValue: Int) {
        num = numValue
        projectedValue = false
    }
}

现在定义一个类SCRange,它有属性width和height,但是我们在使用了TestA这个装饰器去修饰这2个属性的时候,就相当于规定了width和height的最大值就是10。

class SCRange {
    @TestA(numValue: 5) var width: Int
    @TestA(numValue: 5) var height: Int
}

func test1() {
        let range1: SCRange = SCRange()
        print(range1.width)
        range1.width = 50
        print(range1.width)
        print(range1.$width)
    }
// 结果打印:5
// 结果打印:10
// 结果打印:true
综合例子:

在下面这个例子,定义了一个线程安全的Int类型,在多线程的访问中可以保证数据被安全的操作。我们定义了一个卖票的类Ticket和subTicket方法,总票数ticketNum是100张,同时有多个线程在卖票,这个时候我们就要保重多个线程对总票数的安全操作,不会多卖或少卖。

在ThreadSafeNum这个装饰器中,我们定义了NSLock的锁,对num的读写我们都加了锁,保证数据读写多线程的安全。

@propertyWrapper
struct ThreadSafeNum {
    private var num: Int
    let myLock: NSLock = NSLock()
    var wrappedValue: Int {
        get {
            myLock.lock()
            defer {myLock.unlock()}
            return num
        }
        
        set {
            myLock.lock()
            defer {myLock.unlock()}
            num = newValue
        }
    }
    
    init(saftNum: Int) {
        num = saftNum
    }
}

class Ticket {
    @ThreadSafeNum(saftNum: 100) var ticketNum: Int
    
    func subTicket() {
        ticketNum = ticketNum - 1
        if ticketNum <= 0 {
            print("没票了")
        }else {
            print("还剩\(ticketNum)")
        }
    }
}

func test2() {
        let ticket: Ticket = Ticket()
        for _ in 0...199 {
            DispatchQueue.global().async {
                ticket.subTicket()
            }
        }
    }
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 属性 属性将值与特定的类、结构体或枚举关联。存储属性会将常量和变量存储为实例的一部分,而计算属性则是直接计算(而不...
    xiaofu666阅读 3,240评论 0 0
  • 属性 属性将值与特定的类、结构体或枚举关联。存储属性会将常量和变量存储为实例的一部分,而计算属性则是直接计算(而不...
    Upping8阅读 1,306评论 0 0
  • 引言 继续学习Swift文档,从上一章节:结构体和类,我们学习了Swift结构体和类相关的内容,如结构体和类的定义...
    shiyueZ阅读 5,375评论 0 2
  • 在使用 Swift 开发的过程中,经常会遇到诸如 SwiftUI 中的 @State,Combine 中的 @Pu...
    猴子的饼干阅读 9,033评论 1 13
  • 存储属性 - Stored Properties 相当于 OC 的下划线成员变量 适用于:结构体 、 类 类型:常...
    Sunday_David阅读 2,507评论 0 0