propertyWrapper - ShenYj/ShenYj.github.io GitHub Wiki

propertyWrapper

属性包装器最初是在 Swift 5.1 中引入的,作为一种以简单、可重用的方式将额外功能附加到属性的方式

例如,我们我们需要确保某个字段不能为负值

  • 传统的实现方式:

    class Member {
    
        var memberAge: Int {
            get { return self.age }
            set {
                if newValue < 0 { return 0 }
                else { self.age = newValue }
            }
        }
    
        private var age: Int
    
        init(){
            self.age = 0
        }
    
    }
  • 利用属性包装器:

    • 定义一个属性包装器,保存一个 Int 类型的值,并且不能低于0

      @propertyWrapper
      struct NonNegative {
          
          private var number: Int
          var wrappedValue: Int {
              get { return self.number }
              set {
                  if newValue < 0 { self.number = 0 }
                  else { self.number = newValue }
              }
          }
          
          init() { self.number = 0 }
          
      }
    • 使用属性包装器修饰 属性

      class Member {
          
          @NonNegative var age: Int
          
          init() { self.age = 0 }
      }

      若这时我设置一个不满足条件的值时,为将其修正为 0

      let m = Member()
      m.age = -1

在 Swift 5.4 中,它们的行为得到了扩展,以支持将它们用作函数中的局部变量。

@propertyWrapper 
struct NonNegative<T: Numeric & Comparable> {

    var value: T

    var wrappedValue: T {
        get { value }

        set {
            if newValue < 0 {
                value = 0
            } else {
                value = newValue
            }
        }
    }

    init(wrappedValue: T) {
        if wrappedValue < 0 {
            self.value = 0
        } else {
            self.value = wrappedValue
        }
    }
}

从 Swift 5.4 开始,我们可以在常规函数中使用该属性包装器,而不仅仅是附加到一个属性上。例如,我们可能会编写一个游戏,其中我们的玩家可以得到或失去分数,但他们的分数不应低于 0:

func playGame() {
    
    @NonNegative var score = 0

    // player was correct
    score += 4
    // player was correct again
    score += 8
    // player got one wrong
    score -= 15
    // player got another one wrong
    score -= 16

    print(score)
}

playGame()

Property Wrapper 最早并不叫这个名字,而是叫做 Property Behavior,它来自于 Swift 最早期提案 SE-0030。如果看过 Apple Swift 源码的早期版本的话,可以发现里面有较多的 NSCopying 以及 lazy 等重复性的代码。核心团队为了解决这类重复代码问题就提出了 Property Behavior。可以看到这个提案的 State 是 deferred,可能当时核心团队还没有想好如何把这样一个能力做成一个Public 的 API 来对开发者开放。后来社区中有许多人发现这个能力还是很有必要的,可以解决项目中重复代码的问题,提升简洁度和可读性。于是有了 SE-0258 提案,并把提案中 Property Behavior 改名为 Property Wrapper。[摘自 Advanced Property Wrapper in Swift]

⚠️ **GitHub.com Fallback** ⚠️