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

时间:2015-01-10 03:00:19

标签: arrays swift immutability

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

class MyClass{
  var myItems:[String]
}

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

class MyClass{
  let myItems:[String]
}

我的数组是不可变的,但我无法改变分配给它的内容。有没有办法让我的蛋糕也不会改变它?

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

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

......这不是很优雅。

3 个答案:

答案 0 :(得分:24)

我提出了一个比@ matt的优秀答案更简洁的解决方案,它再次利用了访问修饰符。您可以使用private(set)让Swift为您执行此操作,而不是指定单独的私有变量,并公开仅提供getter的公共/内部计算变量:

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

这指定 setter 应该是当前文件范围的私有。 getter 保留默认访问级别(公共/内部),因此外部类仍然可以检索myItems的副本(因为数组是结构的副本,所以通过值*传递)。 / p>

但是你可能会认为你可以在append()上调用像myItems这样的变异方法,并且这样做会在课外修改它(毕竟,我们正在获取{{} 1}},而不是var myItems)。但Swift很聪明,可以认识到数组应该是从let myItems外部不可变的:

MyClass


因此,这实现了与维护私有let object = MyClass() object.myItems.append("change me") // ^ ~~~~~~ // Immutable value of type '[(String)]' only has mutating members named 'append' 的旧Objective-C范例非常相似的效果,并公开了一个公共只读NSMutableArray,它在调用时创建私有数组的不可变副本。更优雅的是,你不觉得吗?


*实际上,Swift在传递值方面非常聪明,甚至可能无法复制数组,直到它被修改为止,即便如此,也只能跟踪变化(内部)。这意味着您可以为数组分配数千个项目,而不会产生巨大的性能损失。

答案 1 :(得分:8)

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

Swift并没有像Cocoa NSArray那样区分mutable和immutable数组。但你为什么需要这个呢? Cocoa 需要它,因为NSArray是一个,所以如果它是可变的,它可以在MyClass的后面更改。但在Swift中,Array是一种值类型,因此不可能发生。

那么你真正试图解决的问题是什么?你应该问问自己,你试图在外面的对象上实现什么合同,并尝试将该合同写入MyClass的结构中。你的结构数组是一个非常合理的方法。但我认为,你在这里所追求的可能实际上是隐私,而不是不可变性。例如,我可能会这样说:

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

现在,假设MyClass在其自己的文件中是独立的(因为Swift中的private意味着对文件是私有的),myItemsHidden可以通过内部以任何方式进行操作> MyClass,所以大概MyClass知道不要改变任何子阵列或者你试图阻止它的任何东西。由MyClass决定它允许自己使用myItemsHidden

但是从外部 MyClass,myItemsHidden不存在;只有myItems,而且任何人都可以做的就是获取并设置它。 (如果他们得到并更改它,原件就不会受到影响。他们不能改变原件。)如果您的目的是阻止任何人在外面设置myItems,请删除设置器功能:

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

现在myItems仅仅是一个分配(已售出)阵列,而不再是。{/ p>

如果想要允许其他类将新数组添加到数组数组中,您可以添加一个MyClass方法,让他们这样做。换句话说,既然这个东西是私有的,那么是什么样的访问权限让你给别人。 MyClass成为其他类可以对此值执行操作的守门员,因为值本身隐藏在private墙后面。

答案 2 :(得分:1)

您可以尝试NSArray。数组本身是不可变的,可以再次赋值变量。

class MyClass{
  var myItems:NSArray
}