如何将不可变数组存储在存储在 属性 中的变量中 Swift?

How to store immutable arrays in a variable stored property in Swift?

我希望我的 class 有一个可以分配不可变数组的存储 属性。如果我这样做:

class MyClass{
  var myItems:[String]
}

我可以为我的 属性 分配不同的数组,但数组是可变的。如果我这样做:

class MyClass{
  let myItems:[String]
}

我的数组是不可变的,但我永远无法更改分配给它的内容。有什么办法可以让我的蛋糕不变异吗?

我想出的最好办法是围绕数组创建一个包装器,然后将该类型用于 属性,如下所示:

class MyClass{
  struct ImmutableWrapper{
    let array:[String]
  }
  var myItems:ImmutableWrapper
}

…这不是很优雅。

你或许可以试试 NSArray。数组本身是不可变的,变量可以重新赋值

class MyClass{
  var myItems:NSArray
}

这个问题很奇怪,因为你的第一个 myItems 不是各种数组的数组,它只是一个字符串数组。不过,我会尝试以更笼统的方式讨论这个问题。

Swift 不像 Cocoa NSArray 那样区分可变数组和不可变数组。但是你为什么需要这个? Cocoa 需要它,因为 NSArray 是 class,所以如果它是可变的,它可以在 MyClass 的后面改变背部。但是在Swift中,Array是值类型,所以不会发生这种情况。

那么您真正要解决的问题是什么?您应该问问自己,您正试图让外部对象履行什么契约,并尝试将该契约写入 MyClass 的结构中。您的结构数组是一种完全合理的方法。不过,我认为您在这里追求的实际上可能是隐私,而不是不变性。例如,我可能会这样说:

class MyClass{
    private var myItemsHidden = [String]()
    var myItems:[String] {
        get {
            return myItemsHidden
        }
        set {
            myItemsHidden = newValue
        }
    }
}

现在,假设 MyClass 单独存在于它自己的文件中(因为 Swift 中的 private 表示文件私有),myItemsHidden 可以通过 inside MyClass,所以大概 MyClass 知道不要改变任何子数组或您试图阻止的任何内容。它允许自己用 myItemsHidden.

做什么取决于 MyClass

但是从外部 MyClass,myItemsHidden不存在;只有 myItems,任何人唯一能做的就是获取并设置它。 (如果他们得到它并更改它,原件不受影响。他们不能改变原件。)如果您的目的是完全防止任何人在外部设置 myItems,请删除 setter 函数:

class MyClass{
    private var myItemsHidden = [String]()
    var myItems:[String] {
        get {
            return myItemsHidden
        }
    }
}

现在 myItems 只是一个分发的(出售的)阵列,仅此而已。

如果想法是允许其他 classes 将新数组添加到数组的数组中,您可以添加一个 MyClass 方法来让他们这样做。换句话说,既然这个东西是私有的,就看你给别人什么样的访问权限了。 MyClass 成为其他 classes 可以用这个值做什么的看门人,因为这个值本身隐藏在 private 墙后面。

我会提出一个比@matt 的优秀答案稍微更简洁的解决方案,它再次利用了访问修饰符。您可以让 Swift 使用 private(set):[=,而不是指定一个单独的私有变量,并公开一个仅提供 getter 的 public/internal 计算变量21=]

class MyClass {
    private(set) var myItems = [String]()
}

这指定 setter 对当前文件范围是私有的。 getter 保留默认访问级别 (public/internal),因此 classes 外部仍然可以检索 myItems 的副本(副本由于数组是结构,因此按值传递*)。

但是你可能会认为你可以在 myItems 上调用像 append() 这样的变异方法,并在这样做时从 class 之外修改它(毕竟,我们'得到 var myItems,而不是 let myItems)。但是 Swift 足够聪明,可以识别出数组在外部应该是不可变的 MyClass:

let object = MyClass()
object.myItems.append("change me")
//     ^       ~~~~~~
// Immutable value of type '[(String)]' only has mutating members named 'append'


因此,这实现了与维护私有 NSMutableArray 的旧 Objective-C 范例非常相似的效果,并公开了一个 public 只读 NSArray,它在以下情况下创建私有数组的不可变副本叫。不过更优雅,你不觉得吗?


*事实上 Swift 在按值传递方面非常聪明,甚至可能在修改数组之前不会复制数组,甚至可能只跟踪更改(内部)。这意味着您可以分配包含数千个项目的数组,而不会导致大量性能损失。