在Swift中如何创建一个可变复制的不可变数组?

22

现在 Swift 的 Array 由于完全的值语义而真正是不可变的,那么我该如何创建一个不可变数组的可变副本呢?就像 Obj-C 的 mutableCopy() 一样。当然,我可以将这个数组强制转换为 NSArray 并使用 mutableCopy(),但是我不想使用 NSArray,因为它没有严格类型。

我有一个工具栏 toolbar,其中有从 storyboard 中获取的 items。我想从工具栏中删除一个项目并使用 toolbar.setItems。我希望不必将它强制转换为 NSArray,因为这些函数都不接受 NSArrays,它们需要 [AnyObject]

显然,现在当我调用 removeAtIndex() 时它不起作用,这是正确的。我只需要一个 mutableCopy。

简单地赋值给 var 对我来说不起作用,并显示 'Immutable value of type [AnyObject]'。

var toolbarItems = self.toolbar.items
toolbarItems.removeAtIndex(2) //Immutable value of type [AnyObject]

我正在使用Beta 3版本。

6个回答

23

问题在于self.toolbar.items是一个隐式解包的可选项(类型为[AnyObject]!),它们始终是不可变的。当您对变量toolbarItems进行赋值时,如果没有明确指定其类型,则它也会成为一个隐式解包的可选项,因此也是不可变的。

要解决这个问题,请执行以下操作之一:

var toolbarItems:[AnyObject] = self.toolbar.items
toolbarItems.removeAtIndex(2)

或者:

var toolbarItems = self.toolbar.items as [AnyObject]
toolbarItems.removeAtIndex(2)

更新

从Xcode 6 Beta 5开始,您可以更新存储在可选变量中的集合,因此原始代码现在可以工作:

var toolbarItems = self.toolbar.items
toolbarItems.removeAtIndex(2)

这基本上就是我在回答中所说的,但是配合了简明扼要的内部机制解释 - 因此你值得我的点赞。 - Antonio

13

数组是值类型(结构体),因此它们是按值而不是按引用传递的。

也就是说,如果你创建一个数组类型的变量并将其分配给不可变数组,则实际上会创建一个不可变数组的副本并将其分配给该变量 - 当然,该副本与原始不可变数组没有任何关系(除了在创建时具有相同的值)。

let immutable = [1, 2, 3]

//immutable[0] = 1 // Fails, ok

var mutable = immutable

mutable[0] = 5

在你的情况下,你正在访问一个不可变数组,它是由AnyObject组成的NSArray(参见文档)。你可以像使用Swift中的数组一样使用它,复制它并进行以下修改:

let immutable : NSArray = [ 1, 2, 3 ]

//immutable[0] = 1 // Fails, ok

var mutable : [AnyObject] = immutable

mutable.removeAtIndex(1) // mutable now is [1, 3]

mutable[0] = 7 // mutable now is [7, 3]

完成更改后,您可以将其分配给 items 属性。


这对我不起作用,我认为是因为beta 3的缘故?:var toolbarItems = self.toolbar.items;toolbarItems.removeAtIndex(2) `//类型为'[AnyObject]'的不可变值。已将编辑添加到问题中。 - Johnston
1
我认为问题是因为 self.toolbar.items 是一个 NSArray。我正在研究它。 - Antonio
1
也许对于NSArray,你需要更加老派一点?尝试使用var mutable = immutable.mutableCopy() as NSMutableArray - Matt Gibson
请阅读我上面回答的补充内容。 - Antonio
1
啊哈,将它输入为 [AnyObject] 就可以了。 - Johnston
我们可以在这里使用Userdefalt()值作为“不可变”Nsarray,而不是直接使用数组。 - Ram Madhavan

1
这很简单,只需使用var声明您的数组即可。
var items = toolbar.items

现在你可以更改项目,然后重新分配到工具栏。
toolbar.items = items

请注意,自Beta 3版本以来,您无法更改使用let声明的“不可变”数组的元素。仅可以固定数组的长度,这就是为什么您无法删除项目的原因。
然而,根据苹果的UIToolbar文档,items数组已经是可变的。

SWIFT
var items: [AnyObject]!


请看我的编辑。我不确定这是因为Beta 3的原因吗? - Johnston
您最新的编辑(使用var b = a)在Xcode构建6A254o中运行良好。如果您尝试a[1] = 5,您会发现b按预期被更改。 - Mundi
是的,我很蠢,我不是故意打那个的。我已经删除了它。虽然这样做可以工作,但出于某种原因,self.toolbar.items 仍然是不可变的。而且我认为,如果你分配它们,数组现在完全是不可变的。从苹果发布说明中可以看到:“Swift 中的数组已经被完全重新设计,具有与字典和字符串一样的完整值语义。这解决了各种可变性问题 - 现在 'let' 数组是完全不可变的,而 'var' 数组是完全可变的。” - Johnston
1
@Mundi,我认为在beta 3中这不再有效,他们解决了这个问题。在一个不可变数组中,您不能更改元素的值。 - Antonio
看我的额外回答 - 我测试过了,它可以工作。 - Mundi

1

测试成功:

    var mutable : [UIBarButtonItem] = []
    for button in toolbar.items {
        mutable += button as UIBarButtonItem
    }
    mutable.removeAtIndex(2)
    toolbar.setItems(mutable, animated: true)

1
从数组中删除特定索引处的对象。
let fullArray : NSArray = Userdefaults().value(forKey: "YOUR_ARRAY_STRING") as! NSArray
var mutableArray : [AnyObject] = fullArray as [AnyObject]
mutableArray.remove(at: INDEX_TO_REMOVE) //Eg: mutableArray.remove(at: 0)
mutableArray.append(ARRAY_TO_APPEND)

0
在Beta3中,常量数组是完全不可变的,而变量数组则是完全可变的。因此,只需将let array更改为var array,然后验证您的代码即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接