Swift中的didSet对变异函数有一种奇怪的连锁效应

dis*_*ute 16 ios swift

我刚刚得知变异func只是一个带有第一个参数的curful func,因此下面的代码将起作用并firstName改为"John"

struct Person {
    var firstName = "Matt"

    mutating func changeName(fn: String) {
        firstName = fn
    }
}
var p = Person() 
let changer = Person.changeName
changer(&p)("John")
p.firstName
Run Code Online (Sandbox Code Playgroud)

但奇怪的事情发生在我添加属性观察者p如下,你可以看到firstName仍然是"马特",为什么? 在此输入图像描述

Nik*_*uhe 5

一个有趣的注意事项是在调用curried setter之前调用观察者:

struct Person {
    var firstName = "Matt"

    mutating func changeName(fn: String) {
        firstName = fn
    }
}

var p: Person = Person() {
    didSet {
        print("p was set")
    }
}

print("1: \(p.firstName)")
let changer = Person.changeName
print("2: \(p.firstName)")
let setter = changer(&p)
print("3: \(p.firstName)")
setter("John")
print("4: \(p.firstName)")
p.changeName("John")
print("5: \(p.firstName)")
Run Code Online (Sandbox Code Playgroud)

这打印:

1: Matt
2: Matt
p was set
3: Matt
4: Matt
p was set
5: John
Run Code Online (Sandbox Code Playgroud)

因此,似乎在inout结构中获取setter方法会执行实际的变异.这可以通过inout参数如何在语义上工作来解释:当参数传递给函数时,它的值被复制到函数可以改变它的位置.当函数返回时,该值将被复制回原始位置,触发setter观察者一次,无论值是否发生了变化.

当我们改变将预先填好的咖喱setter的方式:

let setter = p.changeName
Run Code Online (Sandbox Code Playgroud)

...编译器对象:

error: partial application of 'mutating' method is not allowed
Run Code Online (Sandbox Code Playgroud)

似乎编译器理解关闭inout值是一个坏主意,因为它基本上是对值类型的引用.

闭包可以让你随时改变struct的值,即使编译器认为它是常量的.为了防止这种不幸的情况,编译器只是禁止关闭inout.

您找到了一个欺骗编译器并解决诊断问题的案例.这似乎是一个错误,应该提交.

精简版:

struct Foo {
    mutating func foo() {}
}

var f = Foo()
let m = Foo.foo
let s = m(&f)
Run Code Online (Sandbox Code Playgroud)

最后两行中的一行应该发出错误,类似于let x = f.foo.

  • 我不想让你添加它!:) - 非常好的分析.我可以建议您提交错误报告,因为OP可能会发现这令人生畏吗?并祝贺OP找到了一个很好的bug.顺便说一句,这并不是我第一次以令人惊讶的方式看到添加一个setter观察者改变行为. (2认同)

dis*_*ute 1

在最新接受的提案0042-flatten-method-type中,self不再作为柯里化函数传递,因此这个问题在 Swift 3 中得到了解决