iOS開發(fā) - 「Swift 學習」Swift屬性包裝器@propertyWrapper 投影值 projectedValue(二)

包裝器投影值 projectedValue

projectedValue 為 property wrapper 提供了額外的功能(如:標志某個狀態(tài),或者記錄 property wrapper 內部的變化等)

示例一:通過projectedValue標記屬性是否被修正過

    @propertyWrapper
    struct TwelveOrLess {
        
        private var number = 0
        // 包裝器的投影值,標記數(shù)值修改狀態(tài)
        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
                }
            }
        }
        
        // 初始化方法 參數(shù)一定要叫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

三、帶參數(shù)的屬性包裝器

    @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)
            }
        }
     
        // 參數(shù)一定要叫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必須在第一個參數(shù)
        @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}{歡迎大佬兒來指正糾錯,共同學習???。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容