【问题标题】:Swift: Pass array by reference?Swift:通过引用传递数组?
【发布时间】: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 }

标签: arrays swift pass-by-reference


【解决方案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 中的行为非常不同。见stackoverflow.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 应该只在用例需要的极少数情况下用作优化。
        猜你喜欢
        • 1970-01-01
        • 2011-08-09
        • 2020-07-30
        • 2012-04-17
        • 2020-03-09
        • 2019-06-29
        • 2015-07-04
        • 2011-05-24
        相关资源
        最近更新 更多