Swift 2.0' inout'函数参数和计算属性

时间:2015-07-05 15:55:58

标签: swift swift2

我现在正在测试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。

3 个答案:

答案 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)

@Martin R在上述thread上指出了我,特别是由Chris Lattner(斯威夫特的主谋)做出的评论#16,这有助于我理解&#39; inout&#39;行为。谢谢你!

考虑以下代码:

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的回应。