Swift:将属性包装器附加到函数参数上的教程

327 阅读2分钟

Swift 5.5中的新功能: 属性包装器现在可以直接应用于函数参数,就像它们可以用来为一个属性或局部变量添加额外功能一样。

例如,假设我们正在开发的一个应用程序包含一个为给定名称保存Photo 的函数,而我们总是想通过小写字母来规范每个名称。做到这一点的一个方法是使用一个本地属性覆盖传递的name 参数--像这样:

func savePhoto(_ photo: Photo, named name: String) {
    let name = name.lowercased()
    ...
}

上述方法当然没有错,但让我们看看我们现在如何使用一个属性包装器将我们的小写转换嵌入到函数参数本身。

为了开始,让我们创建一个Lowercased 属性包装器,它可以自动降低分配给它的任何String 值的大小写:

@propertyWrapper struct Lowercased {
    var wrappedValue: String {
        didSet {
            wrappedValue = wrappedValue.lowercased()
        }
    }

    init(wrappedValue: String) {
        self.wrappedValue = wrappedValue.lowercased()
    }
}

然后,我们现在可以将新的Lowercased 包装器直接附加到我们的name 参数上,这将确保它的值永远是小写的,而不需要在我们的函数主体中进行这种转换。

func savePhoto(_ photo: Photo, @Lowercased named name: String) {
    ...
}

很整洁即使是接受自己参数的属性包装器也可以使用上述技术进行连接。例如,下面这个Truncated 包装器会自动截断其包装的集合,使其只包含一定数量的元素。

@propertyWrapper struct Truncated<Value: RangeReplaceableCollection> {
    var wrappedValue: Value {
        didSet { wrappedValue = Value(wrappedValue.prefix(maxLength)) }
    }
    var maxLength: Int

    init(wrappedValue: Value, maxLength: Int) {
        self.wrappedValue = Value(wrappedValue.prefix(maxLength))
        self.maxLength = maxLength
    }
}

有了上面的方法,我们现在可以很容易地截断任何基于集合的函数参数,只需将我们新的Truncated 包装器附加到它上面--像这样:

func updateFavorites(@Truncated(maxLength: 20) to favorites: [Item]) {
    ...
}

虽然这个新功能并没有为我们提供任何革命性的新方法来编写 Swift 代码,但它确实为 Swift 的整个属性包装器系统增加了额外的一致性--因为包装器现在可以应用于属性、局部变量和函数参数,都具有相同的功能。