【问题标题】:Inout and NSMutableDictionaryInout 和 NSMutableDictionary
【发布时间】:2020-10-26 21:45:23
【问题描述】:

如果我在 XCode 12 Playground (Swift 5.3) 中运行以下代码,我会从两个列表中得到相同的结果:

import Foundation

var dict = NSMutableDictionary()

dict["x"] = 42

func stuff(_ d: inout NSMutableDictionary) {
    d["x"] = 75
}

stuff(&dict)

dump(dict) // x is 75

另一个:

import Foundation

var dict = NSMutableDictionary()

dict["x"] = 42

func stuff(_ d: NSMutableDictionary) {
    d["x"] = 75
}

stuff(dict)

dump(dict) // x is 75 still

根据此处的文档,第二个清单应该给我一个错误: https://docs.swift.org/swift-book/LanguageGuide/Functions.html

但它仍然有效。

这是因为这些 in-out 规则的执行仅限于 Swift 类型,而 Cocoa 类型除外?

【问题讨论】:

    标签: swift inout


    【解决方案1】:

    这不是因为 Cocoa 类型被豁免,而是因为 NSMutableDictionaryclass(而不是 struct),而 inout 并不是指您的想法。

    不幸的是,您链接到的文档(以及更多 in-depth documentation on inout parameters it links to)并没有明确“价值”的真正含义:

    in-out参数有一个值,传入函数,被函数修改,传回函数外替换原来的值

    下面的陈述稍微暗示了一点,但可能更清楚:

    您只能将变量作为输入输出参数的参数传递。您不能将常量或文字值作为参数传递,因为无法修改常量和文字。

    文档描述的“值”是作为inout 传递的变量。对于值类型 (structs),这是有意义的,因为每个持有这些类型值的变量实际上都持有该值的一个副本

    var a = MyGreatStruct(...)
    var b = a
    // a and b are not directly linked in any way
    

    struct 传递给函数通常复制 值到一个新的局部变量(新变量= 复制),而您可以想象inout 让您直接访问原始 变量(没有新变量)。

    没有描述的是,对于行为不同的类,效果是相同的。

    let a = MyGreatClass(...)
    let b = a
    // modifying `a` will modify `b` too since both point to the same instance
    

    class 传递给函数也会将变量复制到新的局部变量中,但是复制没有意义——两个变量都持有相同的东西:引用 em> 到内存中的对象本身。从这个意义上说,复制并没有做任何特别的事情,您可以从函数内部修改对象,就像从外部修改对象一样。类的inout 的行为方式与structs 相同:它通过引用传递原始变量。无论如何,这与您想要对对象执行的大多数操作无关(尽管它确实允许您将变量指向函数内的 不同 对象):

    var a = MyGreatClass("Foo")
    
    // func foo(_ value: MyGreatClass) {
    //     value = MyGreatClass("Bar") // <- not allowed since `value` isn't mutable
    // }
    
    func foo(_ value: inout MyGreatClass) {
        value = MyGreatClass("Bar")
    }
    
    print(ObjectIdentifier(a)) // <some pointer>
    foo(&a)
    print(ObjectIdentifier(a)) // <some other pointer>
    

    【讨论】:

    • 那么为什么 NSMutableDictionary 数组是一个值类型呢?
    • @zaitsman Array&lt;T&gt; 是一个值类型(不管T),因为它是一个维护值语义的struct。不管它持有什么,你都会得到相同的效果:例如,let a = Array(...) 不会是可变的。然而,数组里面的东西可能是。例如,Array&lt;NSMutableDictionary&gt; 是一个包含引用类型的值类型。我无法将a 重新分配给另一个数组,但我可以写a[0]["my_key"] = &lt;whatever&gt;。这不会改变数组,但会将对象 a[0] refers 更改为。
    • @zaitsman 你可以想象a[0]["my_key"] = &lt;whatever&gt; 是这样的:let dict: NSMutableDictionary = a[0]; dict["my_key"] = &lt;whatever&gt;。 (请注意 dict 最初在 a 内部并不重要吗?因为 a[0] 是一个指针,而不是整个字典值。)
    • 不管怎样,需要以这种方式混合值类型容器和引用类型容器的情况比较少见。对于大多数用例,ArrayDictionary 是 Swift 的选择。 NSArray/NSMutableArrayNSDictionary/NSMutableDictionary 肯定有用例,但它们不是常态。
    • 不幸的是,我的应用程序是建立在这个第三方 sdk 之上的,它从 Cocoaland 吐出这些容器,因此整个应用程序“不是标准”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多