是 Swift 按值传递还是按引用传递

Is Swift Pass By Value or Pass By Reference

提问人:gran_profaci 提问时间:12/9/2014 更新时间:3/23/2022 访问量:107746

问:

我真的是 Swift 的新手,我刚刚读到类是通过引用传递的,数组/字符串等是复制的。

通过引用传递的方式是否与在 Objective-C 或 Java 中实际传递“a”引用的方式相同,或者它是否正确通过引用传递?

Swift 引用 按值 传递指针

评论

0赞 newacct 12/9/2014
“引用传递的方式是否与 Objective-C 或 Java 中的引用方式相同” Objective-C 和 Java 都没有引用传递。
2赞 gran_profaci 12/9/2014
是的。我知道那件事。您不会通过引用。按值传递引用。我以为在回答时就知道了。
0赞 6rchid 5/15/2020
Java 通过值传递,而不是引用。

答:

70赞 Nate Cook 12/9/2014 #1

默认情况下,Swift 中的所有内容都是通过“copy”传递的,所以当你传递一个值类型时,你会得到一个值的副本,当你传递一个引用类型时,你会得到一个引用的副本,以及所有这意味着的。(也就是说,引用的副本仍指向与原始引用相同的实例。

我在上面的“副本”周围使用恐吓引号,因为 Swift 做了很多优化;在可能的情况下,它不会复制,直到出现突变或突变的可能性。由于参数在默认情况下是不可变的,这意味着大多数时候实际上不会发生复制。

评论

6赞 MrAn3 5/1/2020
对我来说,这是最好的答案,因为它阐明了例如,可以在函数内部修改实例属性,即使参数是副本(按值传递),因为它指向相同的引用。
50赞 newacct 12/9/2014 #2

当参数不 时,它始终按值传递的。inout

如果参数为 ,则始终是按引用传递的。但是,由于在传递给参数时需要在参数上显式使用运算符,因此它可能不符合传统的按引用传递定义,即直接传递变量,因此这有点复杂。inout&inout

评论

3赞 Gobe 3/22/2016
这个答案,结合 Nate Cook 的答案,对我来说(来自 C++)更清楚,即使是“引用类型”也不会在函数范围之外被修改,除非您明确指定它(通过使用inout)
17赞 Dalija Prasnikar 4/14/2017
inout其实不是通过引用传递,而是复制进复制出来,它只保证函数调用后修改后的值会赋给原来的参数。输入输出参数
3赞 MrAn3 5/1/2020
虽然一切都是按价值传递的。可以在函数内修改引用类型属性,作为对同一实例的复制引用。
202赞 matt 12/9/2014 #3

Swift 中的事物类型

规则是:

  • 类实例是引用类型(即类实例的引用实际上是一个指针)

  • 函数是引用类型

  • 其他一切都是值类型;“其他一切”只是指结构体的实例和枚举的实例,因为这就是 Swift 的全部内容。例如,数组和字符串是结构体实例。正如 newacct 所指出的那样,您可以通过使用和获取地址来传递对其中之一的引用(作为函数参数)。但该类型本身就是一个值类型。inout

参考类型对您意味着什么

引用类型对象在实践中是特殊的,因为:

  • 仅仅赋值或传递给函数就可以产生对同一对象的多个引用

  • 对象本身是可变的,即使对它的引用是一个常量(显式或隐含)。let

  • 对象的突变会影响该对象,如对它的所有引用所看到的那样。

这些可能是危险的,所以要留意。另一方面,传递引用类型显然是有效的,因为只复制和传递指针,这是微不足道的。

价值类型对您意味着什么

显然,传递值类型是“更安全”的,这意味着它所说的:你不能通过引用来改变结构实例或枚举实例。另一方面,这种安全性是通过单独复制值来实现的,不是吗?这难道不会使传递值类型的成本可能很高吗?letlet

嗯,是也不是。它并不像你想象的那么糟糕。正如 Nate Cook 所说,传递值类型并不一定意味着复制,因为(显式或隐式)保证了不可变性,因此无需复制任何内容。即使传递到引用中也不意味着事物将被复制,只是在必要时它们可以被复制(因为存在突变)。文档特别建议您不要弄乱内裤。letvar

评论

7赞 newacct 12/9/2014
“类实例是通过引用传递的。函数是通过引用传递的“不。当参数不考虑类型时,它是按值传递的。某物是否按引用传递与类型正交。inout
8赞 matt 12/9/2014
@newacct 嗯,从严格意义上讲,你当然是对的!严格来说,应该说一切都是按值传递的,但枚举实例和结构实例是类型,类实例和函数是引用类型。例如,请参阅 developer.apple.com/swift/blog/?id=10 - 另请参阅 developer.apple.com/library/ios/documentation/Swift/Conceptual/...但是,我认为我所说的符合这些词的一般含义。
8赞 newacct 12/9/2014
对,值类型/引用类型不应与按值传递/按引用传递混淆,因为值类型可以按值传递或按引用传递,引用类型也可以按值传递或按引用传递。
1赞 johnbakers 3/28/2021
@newacct 我也认为这些评论具有误导性。例如,说“引用类型也可以按值或引用传递”。从语义上讲,引用类型中的数据始终通过引用 -- 句点传递。引用(指针)是按值传递的,但程序员的语义是它总是按引用传递,总是。不确定建议它们也可以按值传递是什么意思。即使无法重新分配参照,其所有数据也始终是参照。
1赞 matt 8/29/2021
@MikeKeskinov 我不同意。如果参数是引用类型,则编译器不会告诉您任何此类内容。如果你有 ,那么你可以说 。如果这不是你所看到的,那么你就没有引用类型。class Dog {var name = "fido "}func f(_ dog: Dog) {dog.name = "rover"}
7赞 whyceewhite 8/5/2015 #4

Apple Swift 开发者博客有一篇名为“值和引用类型”的文章,对这个主题进行了清晰而详细的讨论。

引用:

Swift 中的类型分为两类:第一类是“值类型”, 其中每个实例都保留其数据的唯一副本,通常定义 作为结构、枚举或元组。第二种是“引用类型”,其中 实例共享数据的单个副本,类型通常为 定义为类。

Swift 的博客文章继续通过示例解释这些差异,并建议何时使用其中一个而不是另一个。

评论

3赞 Jörg W Mittag 10/5/2016
这并不能回答这个问题。问题是关于按值传递与按引用传递,这与值类型与引用类型完全正交。
1赞 Bahram Zangene 11/1/2017 #5

默认情况下,类按引用传递,其他类按值传递。 您可以使用关键字通过引用进行传递。inout

评论

2赞 Hannes Hertach 6/30/2020
这是不正确的。 是一个 copy in, copy out 运算符。它将首先复制对象,然后在函数返回后覆盖原始对象。虽然看起来相同,但也有细微的差异。inout
18赞 Chris Amelinckx 11/15/2017 #6

下面是一个用于引用传递的小代码示例。 避免这样做,除非你有充分的理由这样做。

func ComputeSomeValues(_ value1: inout String, _ value2: inout Int){
    value1 = "my great computation 1";
    value2 = 123456;
}

这样称呼它

var val1: String = "";
var val2: Int = -1;
ComputeSomeValues(&val1, &val2);

评论

1赞 Brainless 3/2/2020
为什么要避免这样做?
2赞 Chris Amelinckx 4/16/2020
@Brainless,因为它增加了代码不必要的复杂性。最好将参数输入并返回单个结果。必须这样做,通常表明设计不佳。另一种说法是,传入的引用变量中隐藏的副作用对调用者来说是不透明的。
4赞 Hannes Hertach 6/30/2020
这不会通过引用传递。 是一个 copy in, copy out 运算符。它将首先复制对象,然后在函数返回后覆盖原始对象。虽然看起来相同,但也有细微的差异。inout
3赞 Jules Burt 4/16/2019 #7

当您将 inout 与中缀运算符(如 +=)一起使用时,可以忽略 &address 符号。我猜编译器假设通过引用传递?

extension Dictionary {
    static func += (left: inout Dictionary, right: Dictionary) {
        for (key, value) in right {
            left[key] = value
        }
    }
}

origDictionary += newDictionaryToAdd

而且,这本词典“添加”也只写到原始引用,非常适合锁定!

2赞 mohsen 8/14/2019 #8

类和结构

结构和类之间最重要的区别之一是,在代码中传递结构时,结构总是被复制的,但类是通过引用传递的。

闭 包

如果将闭包分配给类实例的属性,并且该闭包通过引用该实例或其成员来捕获该实例,则将在闭包和实例之间创建一个强引用循环。Swift 使用捕获列表来打破这些强大的引用周期

ARC(自动参考计数)

引用计数仅适用于类的实例。结构和枚举是值类型,而不是引用类型,并且不通过引用进行存储和传递。

0赞 yoAlex5 11/21/2020 #9

Swift ,以及 for 的引用值和 copy 的值assignpassreturnreference typeValue Type

[值与引用类型]

如果与你比较,你可以找到匹配项:Java

  • Java 引用类型(所有对象)
  • Java primitive type(int, bool...) - Swift 使用struct
-1赞 Nadia Tariq 3/23/2022 #10

struct 是一种值类型,因此它始终作为值传递。让创建结构体

    //STEP 1 CREATE PROPERTIES
struct Person{
    var raw : String
    var name: String
    var age: Int
    var profession: String
    // STEP 2 CREATE FUNCTION
    func personInformation(){
        print("\(raw)")
        print("name : \(name)")
        print("age : \(age)")
        print("profession : \(profession)")
    }
}
//allow equal values
B = A then call the function
A.personInformation()
B.personInformation()
print(B.name)

当我们更改“B”的值时,它的结果相同,因为 A 的值被复制了,因此 B 中发生了更改,例如 B.name = “Zainab” B的名字发生了变化。它是按价值传递

按引用传递 类总是使用通过引用传递,其中只复制占用内存的地址,当我们像结构一样改变B的值时,A和B都因为引用被复制而改变。