【问题标题】:How do you create an immutable array in Swift?如何在 Swift 中创建不可变数组?
【发布时间】:2014-07-28 05:59:47
【问题描述】:

如何在 Swift 中创建不可变数组?

对文档的肤浅阅读表明您可以这样做

let myArray = [1,2,3]

但遗憾的是,这实际上产生了一个可变的、固定大小的数组。这种可变性创造了通常的谜题,带有意想不到的别名和改变其参数的函数:

let outterArray = [myArray, myArray]
outterArray[0][0] = 2000
outterArray //=> [[2000,2,3],[2000,2,3]]   surprise!

func notReallyPure(arr:Int[]) -> () { arr[0] = 3000 }
notReallyPure(myArray)
myArray // => [3000,2,3]

并不比 C 好多少。

如果我想要不变性,最好的选择真的是像这样将它包装在 NSArray 中:

let immutableArray = NSArray(myArray: [1,2,3])

这看起来很疯狂。我在这里错过了什么?

更新(2015-07-26):

这个问题可以追溯到 Swift 的早期。从那以后,Swift 已经更新,因此不可变数组实际上是不可变的,如下面的答案所示。

【问题讨论】:

  • 不幸的是,我认为您并没有遗漏什么。恕我直言,这是 Swift 的一个坏特性。
  • 我很确定使用元组将保证不变性,尽管我意识到这失去了数组的好处
  • 我希望比我更聪明的人理解一个很好的理由。这看起来很疯狂。
  • 恕我直言,Swift 是一种非常好的语言,但字符串、数组和字典的设计非常糟糕。
  • @Jeff,我认为这很疯狂,因为我认为不变性应该是默认设置。如果 Swift 要麻烦自己拥有自己的集合类型,而不是仅仅使用 Cocoa,那么我们就不需要使用 Cocoa 来获取不可变数组。

标签: arrays swift


【解决方案1】:

这在 Xcode 6 beta 3 中发生了变化。虽然数组过去是半可变的,正如您所描述的,它们的元素是可变的,但它们的长度是固定的,但现在不可变数组与字典共享相同的值语义:

来自 Xcode 6 beta 3 发行说明:

• Swift 中的数组已经过完全重新设计,具有全值语义,就像 Swift 中一直拥有的 Dictionary 和 String 一样。这解决了各种可变性问题——现在“let”数组是完全不可变的,而“var”数组是完全可变的——可以与 Dictionary 和 String 正确组合,并解决其他更深层次的问题。如果您习惯于 NSArray 或 C 数组,值语义可能会令人惊讶:数组的副本现在使用高效的惰性复制实现生成所有元素的完整且独立的副本。这是 Array 的一个重大变化,还有一些性能问题需要解决。请 !有关更多信息,请参阅 Swift 编程语言。 (17192555)

Swift 书中有关数组的原始信息已于 2014 年 7 月 7 日更新,以反映 beta 3 的更改。 (如果您像我一样在 Mac 上使用 iBooks,您可能需要删除并重新下载它以获取 7 月 7 日的更新——我无法自动更新。)

【讨论】:

  • 我希望写这篇文章的人脸红了。 "不变性对于数组的含义略有不同" == "但是,对于数组来说,不变性意味着可变性"
  • 我认为您不应该将 Beta 版注释作为机密而非公开共享(您需要 Apple 开发者帐户才能访问它)。
  • @Cthutu 你需要一个开发者帐户才能访问 Xcode 本身的测试版,更不用说发行说明了,人们到处都在讨论它。请记住the NDA has been relaxed this year;讨论的限制与往年不同。
【解决方案2】:

似乎是一个错误,很快就会修复。

引用自Apple dev forum:

问题:

数组和字典的 let 不一致

最终答案:

这被认为是一个错误,而不是一个功能,并将在 稍后的测试版。
-克里斯

【讨论】:

    【解决方案3】:

    对此没有一个很好的答案,而且很奇怪。

    但是,您可以通过调用yourArray.unshare() 来防止数组在流经您的程序时发生意外突变。这会导致数组在分配给新变量时被复制。

    【讨论】:

    • 有趣。但是你不能只在一个变量上调用一个常量数组上的unshare()
    • @algal:是的——就像我说的,这很奇怪。常量数组不能共享,但可以修改。因此,如果要防止意外突变,则必须使数组可变。 Swift 的可变性语义无疑是它最令人讨厌的地方。
    • 这是香蕉。据我所知,没有办法阻止函数修改数组的内容(或断言它不能这样做)。如果函数修改了传递给它的“常量”参数类型数组,则在调用函数之前或之后调用unshare() 无效。你必须信任函数才能做正确的事。
    • 我对常量数组实际上是一个固定长度的可变数组这一事实感到震惊。我想知道苹果是否可以解决这个问题。这可能会使其不向后兼容并改变现有代码的语义。那么就没有希望了吗?永远破碎?
    • 在 Beta 3 中,现在常量数组是真正的常量。
    【解决方案4】:

    现在可以了。

    来自Apple Developer

    如果将数组或字典分配给常量,则该数组或字典是不可变的,其大小和内容无法更改。

    那么现在

    let myArray = [1,2,3]
    

    产生完全不可变数组。耶!

    【讨论】:

      【解决方案5】:

      恕我直言,最简单的解决方法是将其简单地包装在闭包中,如下所示:

      let mutableElements =  [0,1,2,3]
      let reallyImmutable = {[0,1,2,3]}
      println(mutableElements)
      for i in 0..mutableElements.count { mutableElements[i] *= -1 }
      println(mutableElements)    // [0, -1, -2, -3]
      println(reallyImmutable())
      for i in 0..reallyImmutable().count { reallyImmutable()[i] *= -1 }
      println(reallyImmutable())      // [0, 1, 2, 3]
      println(reallyImmutable()[2])   // 2
      let anotherImmutable = { reallyImmutable().map{ $0 * $0 } }
      println(anotherImmutable())     // [0, 1, 4, 9]
      

      您为每次访问的声明和 () 支付额外的 {},但这也让您的代码自己说话。

      可变程序员丹

      附:写了一个包装类ImmutableArray

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多