我现在正在测试Swift 2.0测试版并发现了奇怪的行为。以下是示例代码:
private func someFunc(inout someString: String) {
print("Inside \'someFunc()\'")
print(someString)
someString = "Some another string"
}
private var someAncillaryInt = 42
print(someAncillaryInt)
private var someString: String {
get {
print("Inside \'getter\'")
return "Some string"
}
set {
print("Inside \'setter\'")
someAncillaryInt = 24
}
}
someFunc(&someString)
print(someAncillaryInt)
输出:
42
内部' getter'
内部' someFunc()'
一些字符串
内部' setter'
24
在someString
内打印someFunc()
时,我不明白为什么没有 getter 被调用?为什么在someFunc()
通过时呢?与someString
。
可以假设我还没有理解 inout 参数的复杂性,并且在作为 inout 参数传递后,计算属性将停止为,em,&#34 ;计算",但为什么然后是#setter'我们将另一个值设置为someString
时调用?
谢谢!
UPD :我在下面添加了答案。
更新2015年11月18日:Apple更新了他们的manual,详细说明了如何使用params。
答案 0 :(得分:6)
选择someString
两者都可能导致您的困惑
全局变量的名称,以及作为参数的名称
someFunc()
功能。
print(someString)
内的 someFunc()
打印出来
(本地)函数参数的值,这是完全不相关的
(并隐藏)全局someString
变量。
如果重命名函数参数
,则会更容易理解private func someFunc(inout localString: String) {
print("Inside \'someFunc()\'")
print(localString)
localString = "Some another string"
}
在语义上相同(因此产生相同的输出)。
你可以想到
someFunc(&someString)
如下:
someString
的值(使用getter方法)。someFunc()
执行localString
设置为someString
。someFunc()
返回时,someString
已设置(使用
setter方法)到本地参数的(可能改变的)值
localString
。更多信息可以在Apple Developer Forum的https://devforums.apple.com/thread/230567中找到, 例如:
鉴于getter和setter的保证,inout随后如下: 当使用inout参数调用函数时,逻辑上它调用 在var /下标上获取getter并将值复制到堆栈中 临时的,保证具有物理可寻址性。该 临时的物理地址传递给inout参数 功能 ... 。被叫者 无论它想要什么内存位置(并且永远不知道是否 传入的东西是否计算过)。当被叫者返回时 调用setter将值复制回原位。
和
它还保证属性的getter / setter传入inout 不管它是什么,都会有一次调用它的getter和setter 被调用者(如果访问者有副作用,这很重要) 很贵)。
但也有人说,如有必要,可以避免临时复制。
答案 1 :(得分:1)
在我不明白为什么在someFunc()中打印someString时没有调用getter,为什么someFunc()与someString一起传递。
getter
内打印someString
时没有调用{p> someFunc()
因为已经已被调用。我们已将此字符串作为someString
内部的someFunc()
参数;我们不需要再次获得它。
您的输出显示为:
Inside 'getter' //<-- that's the getter being called!
Inside 'someFunc()'
Some string
Inside 'setter'
您的代码运行:
someFunc(&someString) //<-- that calls the getter!
顺便说一下,这与inout
无关。如果这是一个正常的参数,你会看到同样的事情(就getter
而言)。
答案 2 :(得分:0)
考虑以下代码:
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()&#39;
里面内部吸气器
一些字符串
内部设定者
这42和24
离开`someFunc()&#39;
实际上输出如下:
内部吸气器
在`someFunc()&#39;
里面一些字符串
这42和24
离开`someFunc()&#39;
内部设定者
有点好笑,对吧?
对于具有C ++背景的人来说,这种违反直觉的行为是更基本逻辑的结果,这些逻辑支撑着Swift的许多方面。正如人们可以从Chris的评论中得到的那样,Swift编译器处理 inout 属性的一般方法是在堆栈上创建临时对象以存储原始值(因此,打电话给吸气剂。)
所以,当你操作你的 inout 参数时,你处理的是临时对象(你传递给函数的初始值)但不是原来的(对我个人来说,它是&#39; s有点模糊。但是好的,人们可以解决这个问题。最后:
当被调用者返回时,调用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()&#39;
里面离开功能之前
内部&#39;将设置&#39;与&#39; newValue&#39;:第二个字符串
内部&#39; didSet&#39;与&#39; oldValue&#39;:一些字符串
来自Chris的回复:
它还保证传入inout的属性的getter / setter将调用一次getter和setter,无论被调用者做什么(如果访问者有副作用或价格昂贵,这很重要)
好的,但是如果在某些情况下采用 inout 参数的某些方法不能修改它(不会调用setter或者不修改该临时变量)会怎么样? ,你的名字)?另外,如果我在计算变量的getter和setter背后设想一些重要逻辑并遇到特定情况(想象一下我打开并关闭getter / setter中的文件)会怎么样?最好不要调用getter和setter。
我非常希望在Apple的最终Swift 2.0编程指南中以某种形式看到Chris的回应。