@propertyWrapperSwift5.1新增的语法糖特性,其意图是提高代码复用性——要探究代码复用性提高的缘由,则咱们需要把视角聚集到事务场景中——在事务场景中,开发者对property的操作,除了读写,还常常会为其增加额定的操作代码,比方:

  • property的值增加约束性代码,比方检测赋值是否在某个区间
  • property的读写访问增加数据库操作代码(从数据库中读取值,并在值改动时重写入到数据库)
  • property的读写访问增加其他事务逻辑代码,比方打印日志

当你需要在多个property都增加相同的操作代码的时候,这时候你就可以经过@propertyWrapper这个语法糖工具来处理。@propertyWrapper可以帮助你把property的读写访问代码以及额定的操作代码封装为一个自定义的Property Wrapper(特点包装器),然后让你经过注解的方法把定义的Property Wrapper(特点包装器)应用到各个property上——然后在编译时,编译器会依据Property Wrapper(特点包装器)的描述对被装修了property的代码进行主动转化或者组成,然后完成property操作代码的复用方针。

Swift5之我对@propertyWrapper的思考一

那么如何完成一个Property Wrapper(特点包装器),并把它应用到一个property上呢?建议直接查阅官方教程《Property Wrappers》。下面会经过一个简略示例进行演示,并藉此进行更深一层的考虑共享。

@propertyWrapper
struct TwelveOrLess {
    var wrappedValue: Int {
        didSet {
            wrappedValue = min(wrappedValue, 12)
        }
    }
    init(wrappedValue: Int) {
        self.wrappedValue = min(wrappedValue, 12)
    }
}
struct NormalRectangle {
    var height: Int
    var width: Int
}
struct ConstrainedRectangle {
    @TwelveOrLess var height: Int
    @TwelveOrLess var width: Int
}
func test_TwelveOrLess() {
    let normalRectangle = NormalRectangle(height: 24, width: 24)
    print("\(type(of: normalRectangle.height))") // Prints "Int"
    let constrainedRectangle = ConstrainedRectangle(height: 24, width: 24)
    print("\(type(of: constrainedRectangle.height))") // Prints "Int"
}

上面示例中,经过@propertyWrapper完成了一个自定义的特点包装器@TwelveOrLess,并经过注解的方法装修了ConstrainedRectangleheight特点和width特点,咱们在获取其特点的类型时,发现其类型依然是Int类型。可是,事实是真的这样吗?请看下面断点:

Swift5之我对@propertyWrapper的思考一

ConstrainedRectangleheight特点和width特点在运行时在内存中真实的类型是TwelveOrLess类型,而不是Int类型——这说明,编译器依据自定义的特点包装器主动生成的代码比官方文档和其他博客所描述的要多,并且这部分是过于隐性的——而这正是我想表达和共享的——这些隐性代码在工程中对开发协作的影响:

  • 毫无疑问,@propertyWrapper和根据其完成的自定义的特点包装器带来的正向影响是:满足了代码复用的方针
  • 可是,这个完成是一种隐性完成,可预见的以下的一些负向影响:
    • 接演示的例子,官方获取运行时目标类型的APItype(of:)回来的结果和实践断点的结果纷歧致,这会给开发者,特别是新人开发者带来困惑。
    • 进行代码版本管理时,简单丢掉这种注解,然后引起事务代码出现bug——特别是在大型团队中进行版本代码合并时,不同开发者其水平纷歧,事务上下文纷歧,更简单丢掉这种注解。

那么有没更好的解法呢?这是后续要进一步考虑和探索的。