property wrapper(翻译为中文叫做特点包装器)能够为代码添加了一个层阻隔,这层阻隔是针对property的,添加的方位是:property的界说代码和property如何存储的代码之间

更简单浅显一点的描绘是:

property的存和取的进程中添加了一些代码,这些代码逻辑便是Property Wrapper

Property Wrapper根本写法与作业原理

  • @propertyWrapper标记 + (class或struct或enum)来界说一个Property Wrapper
  • 该结构中有必要声明一个wrappedValue特点,用于表明包装后的值
@propertyWrapper
struct TwelveOrLess {
    private var number = 0
    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, 12) }
    }
}
struct SmallRectangle {
    @TwelveOrLess var height: Int
    @TwelveOrLess var width: Int
}
rectangle.height = 24
print(rectangle.height)
// Prints "12"

根本原理是,编译器会主动将Property Wrapper中的逻辑组成到每个使用的特点上,经过显式的Property Wrapper写法能够看出大致的作业原理

struct SmallRectangle {
    private var _height = TwelveOrLess()
    private var _width = TwelveOrLess()
    var height: Int {
        get { return _height.wrappedValue }
        set { _height.wrappedValue = newValue }
    }
    var width: Int {
        get { return _width.wrappedValue }
        set { _width.wrappedValue = newValue }
    }
}

Property Wrapper高档用法

现在咱们再去审视Property Wrapper,能够更浅显易懂地描绘一下它的作业原理:

在property的set和get进程中,引进一个中间层,这个中间层能够是class、struct或enum中的恣意结构。在这个结构中能够参加恣意逻辑

Property Wrapper的核心既然是这个中间层,那环绕中间层的界说,出现了一些Property Wrapper的高档用法

这些高档用法主要是来自Property Wrapper的不同初始化办法。那么Property Wrapper有几种初始化办法呢?

因为Property Wrapper能够用class、struct、enum恣意类型表明,那初始化办法依照对应type写即可。但是,其中有一种初始化办法比较特别—类似init(wrappedValue:),即初始化办法中包含wrappedValue参数的情况

所以,下面咱们据此分成两类初始化办法进行描绘

  • 系统默许或自界说初始化办法
  • wrappedValue参数的初始化办法

自界说初始化办法

@propertyWrapper
struct SmallNumber {
    private var maximum: Int
    private var number: Int
    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, maximum) }
    }
    init() {
        maximum = 12
        number = 0
    }
}
struct NarrowRectangle {
	@SmallNumber var number: Int
}
  • 上面代码中SmallNumber供给了一个初始化办法
  • NarrowRectanglenumber的写法,便是使用了上述初始化办法
  • 注意一点,因为使用了SmallNumber初始化办法,所以能够认为默许情况下,NarrowRectangle.number是被赋值为0的,所以let abc = NarrowRectangle()写法是不会编译报错的,因为number能够被正确初始化

wrappedValue参数的初始化办法

@propertyWrapper
struct SmallNumber {
    private var maximum: Int
    private var number: Int
    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, maximum) }
    }
    init() {
        maximum = 12
        number = 0
    }
    init(wrappedValue: Int) {
        maximum = 12
        number = min(wrappedValue, maximum)
    }
    init(wrappedValue: Int, maximum: Int) {
        self.maximum = maximum
        number = min(wrappedValue, maximum)
    }
}
struct UnitRectangle {
    @SmallNumber var height: Int = 1
    @SmallNumber var width: Int = 2
    @SmallNumber(wrappedValue: 2, maximum: 5) var  height: Int // 3
    @SmallNumber(maximum: 9) var width: Int = 2 // 4
}
  • 参数中有wrappedValue参数时,为wrappedValue传参的办法比较特别—此处@SmallNumber var height: Int = 1的写法就时将1作为wrappedValue进行传值
  • 原理上讲的话,就等价于UnitRectangle(height: SmallNumber(wrappedValue: 1), width: SmallNumber(wrappedValue: 2))
  • 还有更杂乱的初始化,比方后面两种初始化办法
  • UnitRectangle中3和4写规律使用了第3个初始化办法

总结

  • Property Wrapper在property的存取进程中添加了一个中间层,能够增加自界说逻辑
  • Property Wrapper能够将重复代码进行抽离、复用
  • Property Wrapper的作业原理是编译器主动组成代码
  • Property Wrapper的作业原理要求property有必要是var
  • 基于Property Wrapper原理能够使用于杂乱的场景,比方简化Codable进程–参阅CodableWrapperExCodable代码库