经过 Property Wrappers 简化代码

什么是 Property Wrappers

属性包装器,Swift 的特性之一。用来修饰属性,它能够抽取关于属性重复的逻辑来达到简化代码的目的。好比线程安全检查、将数据存到数据库等。git

如何使用

经过 @propertyWrapper 来标识。下面经过一个例子来讲明它是如何使用的。github

基本使用

假设有一个 SmallRectangle 的结构体,它有 height 和 width 两个属性,须要将两个属性的值都限制在 12 如下。数据库

根据上述的需求,先建立一个 TwelveOrLess 限制条件的结构体:api

@propertyWrapper
struct TwelveOrLess {
    private var number: Int
    init() { self.number = 0 }
    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, 12) }
    }
}
复制代码

在 SmallRectangle 结构体上使用:安全

struct SmallRectangle {
    @TwelveOrLess var height: Int
    @TwelveOrLess var width: Int
}

var rectangle = SmallRectangle()
print(rectangle.height)
// Prints "0"

rectangle.height = 10
print(rectangle.height)
// Prints "10"

rectangle.height = 24
print(rectangle.height)
// Prints "12"
复制代码

经过上面的代码能够看出,使用 @TwelveOrLess 修饰的属性能够自动将值限制在 12 及如下。markdown

那么,当使用 @TwelveOrLess var height: Int 这句代码时,实际发生了什么呢?app

// 这句代码 @TwelveOrLess var height: Int 至关于下面的代码(编译器会自动转为下面的代码):
private var _height = TwelveOrLess()
var height: Int {
    get { return _height.wrappedValue }
    set { _height.wrappedValue = newValue }
}
复制代码

rectangle.height = 24 这句代码的调用路径:函数

  • 调用 SmallRectangle height 的 set 函数。
  • 调用 TwelveOrLess wrappedValue 的 set 函数。
  • 调用 number = min(newValue, 12) 来保证新设置的值小于等于 12。

设置初始值

将上面的 SmallRectangle 改写为下面的代码你会发现报错 - Argument passed to call that takes no argumentsoop

struct SmallRectangle {
    @TwelveOrLess var height: Int = 1
    @TwelveOrLess var width: Int = 1
}
复制代码

这是由于咱们的 TwelveOrLess 并无提供有参的初始化函数。在 TwelveOrLess 添加有参的初始化函数便可解决:spa

init(wrappedValue: Int) {
    number = min(12, wrappedValue)
}
复制代码

假如咱们的条件变量 12 也是能够动态的设置,能够再添加一个初始化函数,用来设置条件变量:

init(wrappedValue: Int, maximum: Int) {
    self.maximum = maximum
    number = min(maximum, wrappedValue)
}
复制代码

使用 init(wrappedValue: Int, maximum: Int) 初始化属性:

struct NarrowRectangle {
    @TwelveOrLess(wrappedValue: 2, maximum: 5) var height: Int
    @TwelveOrLess(wrappedValue: 3, maximum: 4) var width: Int
}

var narrowRectangle = NarrowRectangle()
print(narrowRectangle.height, narrowRectangle.width)
// Prints "2 3"

narrowRectangle.height = 100
narrowRectangle.width = 100
print(narrowRectangle.height, narrowRectangle.width)
// Prints "5 4"
复制代码

Tips:当没有给 @TwelveOrLess 修饰的变量赋初始值时,默认使用 init() 初始化。

struct ZeroRectangle {
    @TwelveOrLess var height: Int
    @TwelveOrLess var width: Int
}

var zeroRectangle = ZeroRectangle()
print(zeroRectangle.height, zeroRectangle.width)
// Prints "0 0"
复制代码

ProjectedValue

projectedValue 用来获取你定义逻辑的一些额外状态值。

好比在上面的例子中,你想获取你设置的值是否超过了限定的最大值,这个就能够用 projectedValue 来获取。

@propertyWrapper
struct TwelveOrLess {
    private var number: Int
    private var maximum: Int
    var projectedValue: Bool
    
    var wrappedValue: Int {
        get { return number }
        set {
            if newValue > 12 {
                number = 12
                projectedValue = true
            } else {
                number = newValue
                projectedValue = false
            }
        }
    }
    
    init() {
        projectedValue = false
        self.number = 0
        self.maximum = 12
    }
    
    init(wrappedValue: Int) {
        projectedValue = false
        maximum = 12
        number = min(maximum, wrappedValue)
    }
    
    init(wrappedValue: Int, maximum: Int) {
        projectedValue = false
        self.maximum = maximum
        number = min(maximum, wrappedValue)
    }
}
复制代码

获取状态值:

struct SomeStructure {
    @TwelveOrLess var someNumber: Int
}
var someStructure = SomeStructure()

someStructure.someNumber = 4
print(someStructure.$someNumber)
// Prints "false"

someStructure.someNumber = 55
print(someStructure.$someNumber)
// Prints "true"
复制代码

经过 $+属性名的方式来获取 projectedValue.

当设值为 4 的时候,没有大于 12,没有触发条件,因此 $someNumber 为 false;当设值为 55 的时候,大于 12,触发了条件,因此 $someNumber 为 true。

实际项目应用

  • 将字符串字符所有小写:
@propertyWrapper
struct LowerLetter {
    private var value = ""
    var wrappedValue: String {
        set {
            value = newValue.lowercased()
        }
        get { return value}
    }
    
    init(wrappedValue: String) {
        value = wrappedValue.lowercased()
    }
}

struct Person {
    @LowerLetter var name: String
}

let p = Person(name: "ABCd")
p.name // abcd
复制代码
// 声明
@propertyWrapper
public final class Delegated1<Input> {
    public init() { self.callback = { _ in } }
    private var callback: (Input) -> Void
    public var wrappedValue: (Input) -> Void { return callback }
    public var projectedValue: Delegated1<Input> { return self }
}

public extension Delegated1 {
    func delegate<Target: AnyObject>(
        to target: Target,
        with callback: @escaping (Target, Input) -> Void) {
        self.callback = { [weak target] input in
            guard let target = target else {
                return
            }
            return callback(target, input)
        }
    }
}

// 使用
final class TextField {
    @Delegated1 var didUpdate: (String) -> ()
}

let textField = TextField()

textField.$didUpdate.delegate(to: self) { (self, text) in
    // `self` is weak automatically!
    self.label.text = text
}
复制代码
相关文章
相关标签/搜索