【问题标题】:How do I make a exact duplicate copy of an array?如何制作数组的精确副本?
【发布时间】:2015-03-04 22:23:36
【问题描述】:

如何制作一个数组的精确副本?

我很难找到有关在 Swift 中复制数组的信息。

我尝试使用.copy()

var originalArray = [1, 2, 3, 4]
var duplicateArray = originalArray.copy()

【问题讨论】:

  • 为什么不直接这样赋值:var duplicateArray = originalArray
  • 这在我的情况下不起作用。这会创建另一个对象,它只是对同一个数组的引用,最终会得到两个引用同一个数组的变量。

标签: arrays swift reference copy


【解决方案1】:

数组在 Swift 中具有完整的值语义,因此不需要任何花哨的东西。

var duplicateArray = originalArray 就是你所需要的。


如果您的数组的内容是引用类型,那么是的,这只会复制指向您的对象的指针。要执行内容的深层复制,您可以改用 map 并执行每个实例的副本。对于符合NSCopying协议的Foundation类,可以使用copy()方法:

let x = [NSMutableArray(), NSMutableArray(), NSMutableArray()]
let y = x
let z = x.map { $0.copy() }

x[0] === y[0]   // true
x[0] === z[0]   // false

请注意,这里有一些陷阱,Swift 的值语义正在努力保护您免受伤害——例如,由于 NSArray 表示一个不可变数组,它的 copy 方法只返回一个对自身的引用,所以上面的测试会产生意想不到的结果。

【讨论】:

  • 我在操场上用这个简单的代码var x = [UIView(), UIView(), UIView()] var y = x for i in x { NSLog("%p", i) } println("---") for i in y { NSLog("%p", i) } 尝试了这个,我得到了这个输出:0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 ---0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 看起来它没有被复制,你知道为什么吗?
  • @PNGamingPower: x 包含地址。 y 包含这些地址的副本。如果你改变 x[0],y[0] 不会改变。 (尝试 x[0] = x[1],y[0] 不会改变)。因此,y 是 x 的深层副本。但是您只复制了指针,而不是它们指向的内容。
  • @ragnarius 所以基本上我们必须定义“复制”的含义,复制指针或值。因此,这是复制/复制指针数组的解决方案,但是如何复制值数组?目标是 x[0] == x[1]x[0] === y[0] 应该失败
  • 这应该是公认的答案,因为 Array 的值语义使得数组的“副本”变得不必要。
  • 这对我不起作用。如果我遵循该方法,我会得到两个最终指向同一个对象数组的引用。如果我从列表中删除一个项目,它会反映在两个对象引用中,因为列表没有被复制,而是对象只是被引用。
【解决方案2】:

内特是正确的。如果您使用原始数组,您只需将 duplicateArray 分配给 originalArray。

为了完整起见,如果您正在使用 NSArray 对象,您将执行以下操作来完成 NSArray 的完整副本:

var originalArray = [1, 2, 3, 4] as NSArray

var duplicateArray = NSArray(array:originalArray, copyItems: true)

【讨论】:

  • 这太棒了!谢谢!
【解决方案3】:

对于普通对象,可以实现一个支持复制的协议,让对象类实现这个协议如下:

protocol Copying {
    init(original: Self)
}

extension Copying {
    func copy() -> Self {
        return Self.init(original: self)
    }
}

然后是用于克隆的数组扩展:

extension Array where Element: Copying {
    func clone() -> Array {
        var copiedArray = Array<Element>()
        for element in self {
            copiedArray.append(element.copy())
        }
        return copiedArray
    }
}

差不多就是这样,查看代码和示例检查gist

【讨论】:

  • 这节省了很多时间,谢谢。
  • 对于子类,协议不能保证需求init是用子类的类型实现的。您正在声明一个为您实现复制的复制协议,但您仍然在实现 clone(),这没有意义。
  • @iGodric copy 用于集合中的元素,而 clone 用于整个集合,在对其元素实现 copy 时可用。有道理?此外,编译器确保子类确实遵循其父类所需的协议。
  • @johnbakers 哦,是的,现在我明白了。感谢您的解释。
  • 非常干净的实现,避免了在object's init 函数中传递任何参数的不必要的麻烦
【解决方案4】:

Nate 的回答还有第三种选择:

let z = x.map { $0 }  // different array with same objects

* 已编辑 * 编辑从这里开始

上面与下面基本相同,实际上使用下面的相等运算符会更好,因为除非更改数组,否则不会复制数组(这是设计使然)。

let z = x

在此处阅读更多信息:https://developer.apple.com/swift/blog/?id=10

* 已编辑 * 编辑到此结束

添加或删除此数组不会影响原始数组。但是,更改数组拥有的任何对象的任何属性都会在原始数组中看到。因为数组中的对象不是副本(假设数组保存对象,而不是原始数字)。

【讨论】:

  • 确实有效,我已经测试过了。有两个数组,如果你改变了1,则影响第二个
  • 不,它没有,除非数组包含原始类型而不是对象。然后它确实会影响答案中的说明。一个简单的测试用例:var array1: [String] = ["john", "alan", "kristen"]; print(array1); var array2 = array1.map { $0 }; print(array2); array2[0] = "james"; print(array1); print(array2);
  • 请参阅我使用自定义类创建的这个要点以获得更好的示例:gist.github.com/oyalhi/3b9a415cf20b5b54bb3833817db059ce
  • 如果您的班级支持NSCopying,则复制一个数组:let z = x.map { $0.copy as! ClassX }
  • 如果使用 Swift 的 BufferPointers,这是您应该将其用作直接副本的版本。在更改原始数组或复制数组中的值之前,Swift 会将原始值复制到副本中,然后继续。如果您改用指针,那么 Swift 现在不会在发生更改时更改,因此您最终可能会更改两个数组。
【解决方案5】:

如果你想复制某个类对象的数组项。 然后,您可以在不使用 NSCopying 协议的情况下遵循以下代码,但您需要有一个 init 方法,该方法应采用对象所需的所有参数。 这是在 Playground 上测试的示例代码。

class ABC {
    
    var a = 0
    func myCopy() -> ABC {
        
        return ABC(value: self.a)
    }
    
    init(value: Int) {
        
        self.a = value
    }
}

var arrayA: [ABC] = [ABC(value: 1)]
var arrayB: [ABC] = arrayA.map { $0.myCopy() }

arrayB.first?.a = 2
print(arrayA.first?.a)//Prints 1
print(arrayB.first?.a)//Prints 2

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-22
    • 1970-01-01
    • 2011-04-21
    • 2019-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多