Swift:通过引用传递数组?

Posted

技术标签:

【中文标题】Swift:通过引用传递数组?【英文标题】:Swift: Pass array by reference? 【发布时间】:2014-08-06 16:56:32 【问题描述】:

我想通过引用将我的 Swift Array account.chats 传递给 chatsViewController.chats(这样当我向 account.chats 添加聊天时,chatsViewController.chats 仍然指向 account.chats)。即,当account.chats 的长度发生变化时,我不希望 Swift 将两个数组分开。

【问题讨论】:

我最终只是将account 设为全局变量并将ChatsViewControllerchats 属性定义为:var chats: [Chat] return account.chats 【参考方案1】:

对于函数参数运算符,我们使用:let(它是默认运算符,因此我们可以省略let)来使参数常量(这意味着我们甚至无法修改本地副本);var 使其可变(我们可以在本地修改它,但不会影响已传递给功能);和inout 使其成为输入输出参数。输入输出实际上意味着通过引用而不是值传递变量。而且它不仅需要通过引用接受值,还需要通过引用传递它,所以使用 & - foo(&myVar) 而不仅仅是 foo(myVar)

传递它

所以这样做:

var arr = [1, 2, 3]

func addItem(_ localArr: inout [Int]) 
    localArr.append(4)


addItem(&arr)    
print(arr) // it will print [1, 2, 3, 4]

确切地说,它不仅仅是一个引用,而是外部变量的真正别名,因此您可以对任何变量类型进行这种技巧,例如使用整数(您可以为其分配新值),尽管它可能这不是一个好的做法,像这样修改原始数据类型可能会造成混淆。

【讨论】:

这并没有真正解释如何使用数组作为引用而不是复制的实例变量。 我认为 inout 使用 getter 和 setter 将数组复制到临时数组,然后在退出函数时将其重置 - 即复制 其实in-out是用copy-in-copy-out或者按值调用的结果。但是,作为一种优化,它可以通过引用使用。 “作为一种优化,当参数是存储在内存中物理地址的值时,函数体内部和外部使用相同的内存位置。优化的行为称为引用调用;它满足所有要求复制输入复制输出模型,同时消除复制开销。” 在 Swift 3 中,inout 的位置发生了变化,即func addItem(localArr: inout [Int]) 另外,var 不再可用于函数参数属性。【参考方案2】:

Swift 中的结构是按值传递的,但您可以使用 inout 修饰符来修改您的数组(请参阅下面的答案)。类通过引用传递。 Swift 中的ArrayDictionary 被实现为结构体。

【讨论】:

Array 在 Swift 中不是按值复制/传递的 - 与常规结构相比,它在 Swift 中的行为非常不同。见***.com/questions/24450284/… @Boon 数组在语义上仍然是复制/传递值,但只是优化为使用 COW。 而且我不推荐使用NSArray,因为NSArray 和Swift 数组存在细微的语义差异(例如引用类型),这可能会导致更多的错误。 这真是要了我的命。我在敲我的头,为什么事情不正常。 如果将inout 与结构一起使用会怎样?【参考方案3】:

为自己定义一个BoxedArray<T>,它实现Array 接口,但将所有功能委托给一个存储的属性。就这样

class BoxedArray<T> : MutableCollection, Reflectable, ... 
  var array : Array<T>

  // ...

  subscript (index: Int) -> T  
    get  return array[index] 
    set(newValue)  array[index] = newValue 
  

在您使用Array 的任何地方使用BoxedArrayBoxedArray 的分配将通过引用进行,它是一个类,因此通过Array 接口对存储属性的更改将对所有引用可见。

【讨论】:

有点可怕的解决方案 :) - 不是很优雅 - 但它似乎可以工作。 嗯,这肯定比回退到“使用 NSArray”来获得“通过引用语义”更好! 我只是感觉将 Array 定义为 struct 而不是 class 是语言设计错误。 我同意。还有一个可憎的地方,StringAny 的子类型,但是如果你 import Foundation 然后 String 成为 AnyObject 的子类型。【参考方案4】:

对于 Swift 版本 3-4 (XCode 8-9),使用

var arr = [1, 2, 3]

func addItem(_ localArr: inout [Int]) 
    localArr.append(4)


addItem(&arr)
print(arr)

【讨论】:

【参考方案5】:

类似

var a : Int[] = []
func test(inout b : Int[]) 
    b += [1,2,3,4,5]

test(&a)
println(a)

???

【讨论】:

我认为问题在于寻求一种方法,让两个不同对象的 properties 指向同一个数组。如果是这种情况,Kaan 的回答是正确的:必须要么将数组包装在一个类中,要么使用 NSArray。 对,inout 仅在函数体的生命周期内有效(无闭包行为) 次要:它是func test(b: inout [Int]) ...也许这是一个旧语法;我在 2016 年才进入 Swift,而这个答案来自 2014 年,所以也许以前的情况有所不同?【参考方案6】:

另一种选择是让数组的使用者根据需要向所有者索取。例如,类似于以下内容的内容:

class Account 
    var chats : [String]!
    var chatsViewController : ChatsViewController!

    func InitViewController() 
        chatsViewController.getChats =  return self.chats 
    



class ChatsViewController 
    var getChats: (() -> ([String]))!

    func doSomethingWithChats() 
        let chats = getChats()
        // use it as needed
    

然后,您可以在 Account 类中随意修改数组。请注意,如果您还想从视图控制器类修改数组,这对您没有帮助。

【讨论】:

【参考方案7】:

使用NSMutableArrayNSArray,它们是类

这样你就不需要实现任何包装器并且可以使用桥接构建

open class NSArray : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration

【讨论】:

【参考方案8】:

使用inout 是一种解决方案,但由于数组是值类型,因此我感觉不是很快捷。从风格上讲,我个人更喜欢返回一个变异的副本:

func doSomething(to arr: [Int]) -> [Int] 
    var arr = arr
    arr.append(3) // or likely some more complex operation
    return arr


var ids = [1, 2]
ids = doSomething(to: ids)
print(ids) // [1,2,3]

【讨论】:

这种事情有一个性能下降。它使用更少的手机电池来修改原始数组:) 我不同意。在这种情况下,调用站点的可读性通常胜过性能损失。从不可变代码开始,然后通过使其可变来进行优化。在某些情况下,您是正确的,但在大多数应用程序中,这是 0.01% 的边缘情况。 我不知道你不同意什么。复制阵列会降低性能,而性能较低的操作确实会使用更多的 CPU,因此会消耗更多的电池。我想你以为我说这是一个更好的解决方案,但我不是。我要确保人们掌握所有可用信息,以便做出明智的选择。 是的,你是对的,毫无疑问 inout 更有效。我以为您是在建议 inout 普遍更好,因为它可以节省电池。我想说这个解决方案的可读性、不变性和线程安全性普遍更好,并且 inout 应该只在用例需要的极少数情况下用作优化。

以上是关于Swift:通过引用传递数组?的主要内容,如果未能解决你的问题,请参考以下文章

Swift 函数是按值还是按引用分配/传递?

Swift 是不是有类似“ref”关键字的东西强制参数通过引用传递?

通过引用或值传递的数组[重复]

C++ 通过引用传递数组

Javascript 是不是通过引用或值将数组传递给函数?

在 C# 中传递数组参数:为啥它是通过引用隐式传递的?