Inout 和 NSMutableDictionary

Inout and NSMutableDictionary

如果我 运行 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

但它仍然有效。

这是因为这些进出规则的实施仅限于 Swift 类型,而 Cocoa 类型是豁免的吗?

这不是因为 Cocoa 类型是豁免的,而是因为 NSMutableDictionaryclass(相对于 struct),而 inout 不是指你可能在想什么。

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

An in-out parameter has a value that is passed in to the function, is modified by the function, and is passed back out of the function to replace the original value

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

You can only pass a variable as the argument for an in-out parameter. You cannot pass a constant or a literal value as the argument, because constants and literals cannot be modified.

文档描述的“值”是作为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 传递给函数也会 将变量复制 到一个新的局部变量中,但复制没有意义——两个变量持有相同的东西: 对内存中对象本身的引用。从这个意义上讲,复制并没有做任何特别的事情,您可以像从外部一样从函数内部修改对象。 类 的 inoutstructs 的行为方式相同:它通过引用传递 原始变量 。这与您想要对对象执行的大多数操作无关(尽管它确实允许您从函数内部使变量指向 different 对象) :

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>