iOS开发 - 「Swift 学习」Swift属性包装器@propertyWrapper 投影值 projectedValue(二)

包装器投影值 projectedValue

projectedValue 为 property wrapper 提供了额外的功能(如:标志某个状态,或者记录 property wrapper 内部的变化等)

示例一:通过projectedValue标记属性是否被修正过

    @propertyWrapper
    struct TwelveOrLess {
        
        private var number = 0
        // 包装器的投影值,标记数值修改状态
        var projectedValue:Bool = false
        
        var wrappedValue: Int {
            get {return number}
            set {
                number = min(newValue, 12)
                if  newValue > 12 {
                    number = 12
                    projectedValue = true
                } else {
                    number = newValue
                    projectedValue = false
                }
            }
        }
        init(wrappedValue: Int = 0) {
            self.number = wrappedValue
        }
    }
    
    struct SmallRectangle {
       
        @TwelveOrLess var height : Int = 4
        @TwelveOrLess var width : Int = 60
    }
        var rectangle = SmallRectangle(height: 10, width: 25)
        rectangle.height = 10
        rectangle.width = 20
        
        print("height = \(rectangle.height) - 是否被修改过 = \(rectangle.$height)")
        print("width = \(rectangle.width) - 是否被修改过 = \(rectangle.$width)")

运行结果:

height = 10 - 是否被修改过 = false
width = 12 - 是否被修改过 = true

示例二:通过projectedValue直接返回self,为propertyWrapper 提供辅助能力,将RGB色值转化为十六进制

    @propertyWrapper
    struct RGBValue {
        
        private var value : Int = 0
        // 通过 projectedValue 直接返回self,为 propertyWrapper 提供辅助能力
        var projectedValue : RGBValue { self }
        
        var wrappedValue: Int {
            get { return value }
            set { value = max(0, min(255, newValue)) }
        }
        
        var hex:String {
            String(format: "%02x", value)
        }
    }
    
    struct RGB {
        
        @RGBValue var r : Int
        @RGBValue var g : Int
        @RGBValue var b : Int
        
        func hexRGB() -> String {
            let rHex = $r.hex // 在属性名前加$访问projectedValue
            let gHex = $g.hex
            let bHex = $b.hex
            return "#\(rHex)\(gHex)\(bHex)"
        }
    }
        var rgb = RGB()
        rgb.r = 32
        rgb.g = 255
        rgb.b = 22
        print(rgb.hexRGB())

运行结果:

#20ff16

总结:

  • projectedValue 可以是任意类型;
  • projectedValue可是存储属性,也可以是计算属性;
  • 两者都是通过实例的属性名进行访问的。不同的是 projectedValue 需要在属性名前加上$才可以访问;
  • wrappedValue: 实例.属性名,属性包装存储的值;
  • projectedValue: 实例.$属性名,映射值;
  • projectedValue的命名是固定的。

@propertyWrapper使用示例

一、验证字符串值是否为空

    @propertyWrapper
    struct CheckEmptyString {
        private var value:String = ""
        var wrappedValue: String {
            get { value }
            set {
                if newValue.isEmpty {
                    //assert(false,"传入的值为空")
                    self.value = "unknowed"
                } else {
                    self.value = newValue
                }
            }
        }
        
        // 初始化方法 参数一定要叫wrappedValue, 不能修改
        init(wrappedValue value: String) {
            self.value = value
        }
    }
    
    
    struct Person {
       //2种写法完全等效, 都会进到 init(wrappedValue value: String) 这个方法
        @CheckEmptyString(wrappedValue: "张三") var name:String // 设置默认值  "张三"
        @CheckEmptyString var job:String = "" // 设置默认值  ""
    }

调用:

        let zhangsan = Person()
        print("name = \(zhangsan.name)")
        print("job = \(zhangsan.job)")

结果:

name = 张三
job = 

二、给UserDefault提供便利的调用方法

   @propertyWrapper
    struct MyUserDefault<T> {
        
        let key : String
        let defaultValue:T
        
        var wrappedValue: T {
            get {
                return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
            }
            set {
                UserDefaults.standard.set(newValue, forKey: key)
            }
        }
        
    }

    enum GlobalSettings {
        
        @MyUserDefault(key: "AD_ENABLED", defaultValue: false) static var isShowADEnabled:Bool
        @MyUserDefault(key: "VersionUpdate_ENABLED", defaultValue: false) static var isShowVersionUpdateEnabled:Bool
        
    }

调用:

        GlobalSettings.isShowADEnabled = true
        print("获取 GlobalSettings.isShowADEnabled = \(GlobalSettings.isShowADEnabled)")
        
        GlobalSettings.isShowVersionUpdateEnabled = false
        print("获取 GlobalSettings.isShowVersionUpdateEnabled = \(GlobalSettings.isShowVersionUpdateEnabled)")

结果

获取 GlobalSettings.isShowADEnabled = true
获取 GlobalSettings.isShowVersionUpdateEnabled = false

三、带参数的属性包装器

    @propertyWrapper
    struct NumberWrapper {
        private var value: Int = 0
        private var maxValue: Int = 10
     
        var wrappedValue: Int {
            get {
                return value
            }
            set {
                // 取 <= maxValue 的值
                value = min(newValue, self.maxValue)
            }
        }
     
        // 参数一定要叫wrappedValue, 不能修改
        init(wrappedValue: Int = 0) {
            self.value = wrappedValue
        }
     
        // 属性包装器的自定义初始化方法
        init(wrappedValue: Int, max: Int) {
            self.maxValue = max
            self.wrappedValue = wrappedValue
        }
    }

    
    struct MyNumber {
        
        // 告诉编译器使用Wrapper包装器包装该属性
        @NumberWrapper var number: Int = 5
     
        // number2和number的效果等价, 注意wrappedValue必须在第一个参数
        @NumberWrapper(wrappedValue: 50, max: 100) var number2: Int
        @NumberWrapper(max: 20) var number3: Int = 50
     
    }

调用:

        let num = MyNumber()
        print("number = \(num.number)")
        print("number2 = \(num.number2)")
        print("number3 = \(num.number3)")

结果:

number = 5
number2 = 50
number3 = 20

这2种写法是完全等价的

  • @NumberWrapper(wrappedValue: 50, max: 100) var number2: Int
  • @NumberWrapper(max: 20) var number3: Int = 50

四、属性加锁

// 属性加锁使用
    @propertyWrapper
    class LockAtomic<T> {
        
        private var value: T
        private let lock = NSLock()
        
        var wrappedValue: T {
            get { getValue() }
            set { setValue(newValue: newValue) }
        }
        
        init(wrappedValue value: T) {
            self.value = value
        }
        
        func getValue() -> T {
            lock.lock()
            defer { lock.unlock() }
            return value
        }
        
        func setValue(newValue:T) {
            lock.lock()
            defer { lock.unlock() }
            
            value = newValue
        }
        
    }

调用:

        @LockAtomic var json : [String:String]?
        json = ["a":"1"]
        print("json = \(String(describing: json))")

结果:

json = Optional(["a": "1"])

\color{gray}{欢迎大佬儿来指正纠错,共同学习😏!!}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容