어레이의 정확한 복제본을 만들려면 어떻게합니까?


100

배열을 정확히 복제하려면 어떻게해야합니까?

Swift에서 배열 복제에 대한 정보를 찾는 데 어려움을 겪고 있습니다.

나는 사용해 보았다 .copy()

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

5
왜 직접이 같은 당신에게 할당 값을 해달라고var duplicateArray = originalArray
다르 메쉬 Kheni

1
제 경우에는 작동하지 않습니다. 그러면 동일한 배열에 대한 참조 인 또 다른 객체가 생성되고 동일한 배열을 참조하는 2 개의 변수가 생성됩니다.
user1060500

답변:


176

배열은 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 다음과 같은 결과가 나타납니다. 복사중인 것처럼 보이지 않습니다 . 이유를 아십니까?
Phil Niedertscheider

@PNGamingPower : x는 주소를 포함합니다. y는 해당 주소의 사본을 포함합니다. x [0]을 변경하면 y [0]은 변경되지 않습니다. (시도 x [0] = x [1], y [0]은 변경되지 않습니다). 따라서 y는 x의 깊은 복사본이지만 포인터가 가리키는 것이 아니라 포인터 만 복사했습니다.
ragnarius

@ragnarius 그래서 기본적으로 우리는 포인터 또는 값을 복사하는 "복사"의 의미를 정의해야합니다. 따라서 이것은 포인터 배열을 복사 / 복제하는 솔루션이지만 값 배열을 어떻게 복제합니까? 목표는 것 x[0] == x[1]하지만 x[0] === y[0]실패
필 Niedertscheider

Array의 값 의미론은 배열의 "복사본"을 불필요하게 만들기 때문에 이것이 허용되는 대답이어야합니다.
Scott Ahten

이것은 나를 위해 작동하지 않습니다. 이 방법을 따르면 동일한 객체 배열을 가리키는 두 개의 참조를 얻습니다. 목록에서 항목을 제거하면 목록이 복사되지 않고 단지 참조 된 것이기 때문에 두 객체 참조 모두에 반영됩니다.
user1060500

28

Nate가 맞습니다. 기본 배열로 작업하는 경우에는 duplicateArray를 originalArray에 할당하기 만하면됩니다.

완전성을 위해 NSArray 객체를 작업하는 경우 다음을 수행하여 NSArray의 전체 복사본을 수행합니다.

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

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

대단합니다! 감사합니다!
Patrick

23

Nate의 대답에 대한 세 번째 옵션이 있습니다.

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

* 편집 됨 * 여기서 편집 시작

위는 본질적으로 아래와 동일하며 실제로 아래의 같음 연산자를 사용하면 배열이 변경되지 않는 한 복사되지 않기 때문에 성능이 더 좋습니다 (설계에 의한 것임).

let z = x

자세한 내용은 https://developer.apple.com/swift/blog/?id=10을 참조하십시오.

* 편집 됨 * 편집은 여기서 끝납니다.

이 배열에 추가하거나 제거해도 원래 배열에는 영향을주지 않습니다. 그러나 배열이 보유하고있는 객체의 속성을 변경하면 원래 배열에서 볼 수 있습니다. 배열의 객체는 복사본이 아니기 때문입니다 (배열이 원시 숫자가 아니라 객체를 보유한다고 가정).


그것은 효과가 있습니다, 나는 그것을 테스트했습니다. 두 가지 배열이되어 U가 1로 변경하면, 제 나아
더러운 나이트

1
배열이 객체 대신 기본 유형을 보유하지 않는 한 그렇지 않습니다. 그러면 답변에 명시된대로 영향을 미칩니다. 간단한 테스트 케이스 :var array1: [String] = ["john", "alan", "kristen"]; print(array1); var array2 = array1.map { $0 }; print(array2); array2[0] = "james"; print(array1); print(array2);
oyalhi

1
사용자 정의 클래스를 사용하여 더 나은 예제를 위해 만든이 요점을 참조하십시오 : gist.github.com/oyalhi/3b9a415cf20b5b54bb3833817db059ce
oyalhi

클래스 NSCopyinglet z = x.map { $0.copy as! ClassX }
John Pang

Swift의 BufferPointers를 사용하는 경우 직접 복사로 사용해야하는 버전입니다. 원본 또는 복사 된 배열의 값을 변경하기 전에 Swift는 원본의 값을 복사본에 복사 한 다음 계속합니다. 대신 포인터를 사용하는 경우 Swift는 변경이 발생하는 경우 또는 언제 변경되지 않으므로 잠재적으로 두 배열을 모두 변경할 수 있습니다.
Justin Ganzer 2019 년

16

일반 객체의 경우 복사를 지원하는 프로토콜을 구현하고 객체 클래스가 다음과 같이이 프로토콜을 구현하도록하는 것이 가능합니다.

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
    }
}

그것은보기 코드를 꽤 많이하고 샘플이 확인 요점을


이것은 많은 시간을 절약했습니다. 감사합니다.
Abhijit

서브 클래스의 경우 프로토콜은 요구 사항 초기화가 서브 클래스의 유형으로 구현된다는 것을 보장 할 수 없습니다. 당신은 당신을 위해 복사를 구현하는 복사 프로토콜을 선언하고 있지만 여전히 clone ()을 구현하고 있습니다.
Binarian

1
@iGodric 복사본은 컬렉션의 요소를위한 것이고, 복제는 전체 컬렉션에 대한 것이며, 해당 요소에 대한 복사본이 구현 될 때 사용할 수 있습니다. 말이 되나? 또한 컴파일러는 하위 클래스가 부모가 요구하는 프로토콜을 따르도록합니다.
johnbakers

@johnbakers 오, 그래, 이제 나는 그것을 봅니다. 설명해 주셔서 감사합니다.
Binarian

매우 깨끗하고 구현은 임의의 파라미터를 전달 불필요한 밀치락 방지 object'sinit 함수
실번 D 애쉬

0

일부 클래스 객체의 배열 항목을 복사하려는 경우. 그런 다음 NSCopying 프로토콜을 사용하지 않고 아래 코드를 따를 수 있지만 개체에 필요한 모든 매개 변수를 가져와야하는 init 메서드가 있어야합니다. 다음은 놀이터에서 테스트 할 예제 코드입니다.

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.