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
设为全局变量并将ChatsViewController
的chats
属性定义为: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 中的Array
和Dictionary
被实现为结构体。
【讨论】:
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
的任何地方使用BoxedArray
。 BoxedArray
的分配将通过引用进行,它是一个类,因此通过Array
接口对存储属性的更改将对所有引用可见。
【讨论】:
有点可怕的解决方案 :) - 不是很优雅 - 但它似乎可以工作。 嗯,这肯定比回退到“使用 NSArray”来获得“通过引用语义”更好! 我只是感觉将 Array 定义为 struct 而不是 class 是语言设计错误。 我同意。还有一个可憎的地方,String
是 Any
的子类型,但是如果你 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】:使用NSMutableArray
或NSArray
,它们是类
这样你就不需要实现任何包装器并且可以使用桥接构建
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:通过引用传递数组?的主要内容,如果未能解决你的问题,请参考以下文章