@Martin R 在上述thread 上指出了我,特别是 Chris Lattner(Swift 的策划者)发表的第 16 条评论,它帮助我理解了“inout”行为。谢谢大佬!
考虑这段代码:
private var someString: String {
get {
print("Inside getter")
return "Some string"
}
set {
print("Inside setter")
}
}
private func someFunc(inout stringArg: String) {
let funcName = "`someFunc()\'"
print("Inside " + funcName)
let someDontMatter0 = 42, someDontMatter1 = 24
print(stringArg)
// sets temporary, not the original one. Hence, no calls to setter
stringArg = "Some other string"
stringArg = "No matter what string"
print("These \(someDontMatter0) and \(someDontMatter1)")
print("Leaving " + funcName)
// when callee returns, calls the setter with "No matter what
// string" as a `newValue'
}
// implicitly creates temporary initialised with `someString' value
// getting from `someString's getter.
someFunc(&someString)
对于有 C++ 背景的人来说,输出看起来应该是这样的:
在`someFunc()'里面
内部吸气剂
一些字符串
内部设置器
这些 42 和 24
离开`someFunc()'
实际上输出如下:
内部吸气剂
在`someFunc()'里面
一些字符串
这些 42 和 24
离开`someFunc()'
内部设置器
有点搞笑,对吧?
对于具有 C++ 背景的人来说,这种违反直觉的行为是更基本的逻辑的结果,它支撑着 Swift 的许多方面。从 Chris 的评论中可以看出,Swift 编译器处理 inout 属性的一般方式是在堆栈上创建 temporary 对象以将原始值存储在那里(因此,调用 getter )。
因此,当您操作 inout 参数时,您处理的是临时对象(具有传递给函数的初始值),而不是原始对象(对我个人而言,这有点模糊。但是好的,可以处理)。最后:
当被调用者返回时,调用setter将值复制回原位
所以,如果对代码稍作改动:
private var someString: String {
get {
print("Inside getter")
return "Some string"
}
set {
print("Inside setter")
}
}
private func someFunc(inout stringArg: String) {
// before returning calls `someString's setter
}
// calls `someString's getter
someFunc(&someString)
输出将是:
内部吸气剂
内部设置器
此外,存储属性也是如此:
private func someFunc() {
var someString = "Some string" {
willSet {
print("Inside \'willSet\' with \'newValue\': \(newValue)")
}
didSet {
print("Inside \'didSet\' with \'oldValue\': \(oldValue)")
}
}
func someOtherFunc(inout stringArg: String) {
print("Inside `someOtherFunc()\'")
stringArg = "First string"
stringArg = "Second string"
print("Before leaving the function")
}
someOtherFunc(&someString)
}
someFunc()
输出:
在`someOtherFunc()'中
离开函数之前
在带有“newValue”的“willSet”内:第二个字符串
在带有“oldValue”的“didSet”中:一些字符串
来自 Chris 的回复:
它还保证传入输出的属性的 getter/setter 将调用其 getter 和 setter 一次,而不管被调用者做什么(如果访问器有副作用或昂贵,这很重要)
好的,但是如果在某些情况下采用 inout 参数的某些方法不修改它(不调用 setter 或不修改该临时变量,你命名它)怎么办?另外,如果我在计算变量的 getter 和 setter 背后构思了一些重要的逻辑并遇到了特定的情况(想象一下我在 getter/setter 中打开和关闭文件)怎么办?最好不要调用 getter 和 setter。
我真的很想在 Apple 的最终 Swift 2.0 编程指南中以某种形式看到 Chris 的回应。