NSArray 딥 복사


119

딥 복사를 허용하는 내장 기능이 NSMutableArray있습니까?

나는 주위를 둘러 보았고 어떤 사람들은 [aMutableArray copyWithZone:nil]작품을 딥 카피 라고 말합니다 . 그러나 나는 시도했고 그것은 얕은 사본 인 것 같습니다.

지금은 for루프를 사용하여 수동으로 복사하고 있습니다 .

//deep copy a 9*9 mutable array to a passed-in reference array

-deepMuCopy : (NSMutableArray*) array 
    toNewArray : (NSMutableArray*) arrayNew {

    [arrayNew removeAllObjects];//ensure it's clean

    for (int y = 0; y<9; y++) {
        [arrayNew addObject:[NSMutableArray new]];
        for (int x = 0; x<9; x++) {
            [[arrayNew objectAtIndex:y] addObject:[NSMutableArray new]];

            NSMutableArray *aDomain = [[array objectAtIndex:y] objectAtIndex:x];
            for (int i = 0; i<[aDomain count]; i++) {

                //copy object by object
                NSNumber* n = [NSNumber numberWithInt:[[aDomain objectAtIndex:i] intValue]];
                [[[arrayNew objectAtIndex:y] objectAtIndex:x] addObject:n];
            }
        }
    }
}

하지만 더 깨끗하고 간결한 솔루션을 원합니다.


44
@Genericrich 딥 및 얕은 복사본은 소프트웨어 개발에서 매우 잘 정의 된 용어입니다. Google.com이 도움이 될 수 있습니다
Andrew Grant

1
-copy변경 불가능한 컬렉션 의 동작이 Mac OS X 10.4와 10.5 사이에서 변경 되었기 때문일 수 있습니다 . developer.apple.com/library/mac/releasenotes/Cocoa/… ( "불변 컬렉션 및 복사 동작"으로 스크롤)
user102008

1
@AndrewGrant 좀 더 생각해 보면 딥 카피 가 잘 정의 된 용어라는 데 동의하지 않습니다 . 읽은 소스에 따라 중첩 된 데이터 구조로의 무제한 재귀가 '전체 복사'작업의 요구 사항인지 여부는 명확하지 않습니다. 즉, 멤버가 원래 개체의 멤버의 얕은 복사 본인 새 개체를 만드는 복사 작업이 '전체 복사'작업인지 여부에 대해 상충되는 답변을 얻을 수 있습니다. 이에 대한 논의는 stackoverflow.com/a/6183597/1709587 을 참조하십시오 (Java 컨텍스트에서하지만 모두 동일하게 관련됨).
Mark Amery

@AndrewGrant @MarkAmery와 @Genericrich를 백업해야합니다. 컬렉션에 사용 된 루트 클래스와 모든 요소가 복사 가능한 경우 전체 복사가 잘 정의 된 것입니다. 이는 NSArray (및 기타 objc 컬렉션)의 경우가 아닙니다. 요소가을 구현하지 않으면 copy"딥 카피"에 무엇을 넣어야합니까? 요소가 다른 컬렉션 인 copy경우 실제로 동일한 클래스의 복사본을 생성하지 않습니다. 그래서 저는 특정한 경우에 원하는 카피의 유형에 대해 논쟁하는 것이 완벽하게 타당하다고 생각합니다.
Nikolai Ruhe 2015 년

@NikolaiRuhe 요소가 NSCopying/를 구현하지 않으면 -copy복사 할 수 없습니다. 따라서 의도 된 기능이 아니기 때문에 복사 할 수 없습니다. Cocoa의 구현 측면에서, 복사 불가능한 객체는 종종 연결된 C 백엔드 상태를 가지므로 객체의 직접 복사를 해킹하면 경쟁 조건 또는 더 나빠질 수 있습니다. 그래서 “ '딥 카피'에 무엇을 넣을 것인가”라고 대답하기 위해 -보유 된 심판. 물건이 없을 때 어디에나 둘 수있는 유일한 물건 NSCopying.
Slipp D. 톰슨

답변:


210

딥 카피에 대한 Apple 문서 에는 다음과 같이 명시되어 있습니다.

1 레벨 전체 사본 만 필요한 경우 :

NSMutableArray *newArray = [[NSMutableArray alloc] 
                             initWithArray:oldArray copyItems:YES];

위의 코드는 멤버가 이전 배열 멤버의 얕은 복사 본인 새 배열을 만듭니다.

전체 중첩 데이터 구조 (링크 된 Apple 문서에서 진정한 딥 카피 라고 부르는)를 깊게 복사해야하는 경우이 접근 방식으로는 충분하지 않습니다. 여기에서 다른 답변을 참조하십시오.


정답 인 것 같습니다. API는 각 요소가 [element copyWithZone :] 메시지를받는다고 설명합니다. 실제로 [NSMutableArray copyWithZone : nil]을 전송해도 딥 복사가되지 않는다는 사실을 알고 있다면이 메서드를 사용하여 배열 배열이 올바르게 복사되지 않을 수 있습니다.
Ed Marty

7
나는 이것이 예상대로 작동하지 않을 것이라고 생각합니다. Apple 문서에서 : "copyWithZone : 메소드는 얕은 복사를 수행합니다 . 임의의 깊이 컬렉션이있는 경우 플래그 매개 변수에 YES를 전달하면 표면 아래 첫 번째 수준의 변경 불가능한 사본이 수행됩니다. NO를 전달하면 첫 번째 수준은 영향을받지 않습니다. 두 경우 모두 더 깊은 수준의 모든 변경 가능성은 영향을받지 않습니다. "SO 질문은 깊은 변경 가능한 복사본에 관한 것입니다.
Joe D' Andrea

7
이 NOT 딥 카피
Daij-Djan

9
이것은 불완전한 답변입니다. 이로 인해 1 단계 전체 복사가 생성됩니다. 배열 내에 더 복잡한 유형이있는 경우 전체 복사를 제공하지 않습니다.
Cameron Lowell Palmer

7
방법에 따라, 딥 카피 할 copyWithZone:수신 클래스에 구현됩니다.
devios1 2014 년

62

이 작업을 쉽게 수행 할 수있는 유일한 방법은 어레이를 아카이브 한 다음 즉시 아카이브를 해제하는 것입니다. 약간의 해킹처럼 느껴지지만 실제로는 컬렉션 복사 에 대한 Apple 문서에 명시 적으로 제안되어 있습니다.

배열 배열이있는 경우와 같이 진정한 딥 카피가 필요한 경우 콘텐츠가 모두 NSCoding 프로토콜을 준수하는 경우 컬렉션을 보관 한 다음 보관 취소 할 수 있습니다. 이 기술의 예가 목록 3에 나와 있습니다.

Listing 3 진정한 딥 카피

NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
          [NSKeyedArchiver archivedDataWithRootObject:oldArray]];

문제는 객체가 NSCoding 인터페이스를 지원해야한다는 것입니다. 이는 데이터를 저장 /로드하는 데 사용되기 때문입니다.

Swift 2 버전 :

let trueDeepCopyArray = NSKeyedUnarchiver.unarchiveObjectWithData(
    NSKeyedArchiver.archivedDataWithRootObject(oldArray))

12
어레이가 큰 경우 NSArchiver 및 NSUnarchiver를 사용하는 것은 성능면에서 매우 무거운 솔루션입니다. NSCopying 프로토콜을 사용하는 일반적인 NSArray 카테고리 메소드를 작성하면 불변 객체의 단순한 '보유'와 변경 가능한 객체의 실제 '복사'가 발생합니다.
Nikita Zhuk

비용에 대해 조심하는 것이 좋지만 NSCoding이 initWithArray:copyItems:메소드 에서 사용되는 NSCopying보다 실제로 더 비쌀 까요? 이 보관 / 보관 해제 해결 방법은 NSCopying이 아닌 NSCoding을 준수하는 제어 클래스 수를 고려할 때 매우 유용합니다.
Wienke 2012-07-28

이 방법을 사용하지 않는 것이 좋습니다. 직렬화는 메모리 복사보다 빠르지 않습니다.
Brett

1
사용자 지정 개체가있는 경우 NSCoding 프로토콜을 준수하도록 encodeWithCoder 및 initWithCoder를 구현해야합니다.
user523234 2013 년

직렬화를 사용할 때는 프로그래머의 재량에 따라 강력히 권장됩니다.
VH-NZZ 2015

34

기본적으로 복사는 얕은 사본을 제공합니다.

호출 copycopyWithZone:NULL기본 영역으로 복사라고도 하는 것과 동일 하기 때문 입니다. copy호출 딥 카피 발생하지 않습니다. 대부분의 경우 얕은 사본을 제공하지만 어쨌든 클래스에 따라 다릅니다. 철저한 토론 을 위해 Apple Developer 사이트 의 Collections Programming Topics 를 추천합니다 .

initWithArray : CopyItems : 1 단계 전체 복사를 제공합니다.

NSArray *deepCopyArray = [[NSArray alloc] initWithArray:someArray copyItems:YES];

NSCoding 딥 카피를 제공하기 위해 Apple에서 권장하는 방법입니다.

진정한 딥 카피 (Array of Arrays)의 NSCoding경우 객체를 필요로 하고 아카이브 / 아카이브 해제합니다.

NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

1
맞습니다. 인기 또는 메모리, 성능에 대해 조기에 최적화 된 엣지 케이스에 관계없이. 제쳐두고, 딥 카피를위한이 서버 해킹은 다른 많은 언어 환경에서 사용됩니다. obj 중복 제거가없는 한 원본과 완전히 분리 된 우수한 전체 복사본을 보장합니다.

1
여기에 적은 rottable 애플 문서의 URL 년대 developer.apple.com/library/mac/#documentation/cocoa/conceptual/...은

7

Dictonary 용

NSMutableDictionary *newCopyDict = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)objDict, kCFPropertyListMutableContainers);

어레 이용

NSMutableArray *myMutableArray = (NSMutableArray *)CFPropertyListCreateDeepCopy(NULL, arrData, kCFPropertyListMutableContainersAndLeaves);


4

아니요,이를 위해 프레임 워크에 내장 된 것이 없습니다. Cocoa 컬렉션은 얕은 복사본 ( copy또는 arrayWithArray:메서드 사용)을 지원하지만 전체 복사 개념에 대해서는 언급하지 않습니다.

컬렉션의 내용이 사용자 지정 개체를 포함하기 시작하면 "깊은 복사"를 정의하기가 어려워지기 때문입니다. "딥 카피는"뜻 모든 객체 그래프의 객체에 대한 고유 참조 기준으로 모든 원래의 객체 그래프의 객체?

가상의 NSDeepCopying프로토콜 이 있다면이를 설정하고 모든 객체에서 결정을 내릴 수 있지만 불행히도 그렇지 않습니다. 그래프에서 대부분의 개체를 제어했다면이 프로토콜을 직접 만들어 구현할 수 있지만 필요에 따라 Foundation 클래스에 범주를 추가해야합니다.

@AndrewGrant의 대답은 keyed archiving / unarchiving의 사용을 제안하는 것은 성능이 좋지 않지만 임의의 객체에 대해 이것을 달성하는 정확하고 깨끗한 방법입니다. 이 책은 지금까지 진행되었으므로 딥 카피를 지원하기 위해 정확히 해당하는 모든 객체에 범주를 추가하는 것이 좋습니다 .


2

JSON 호환 데이터에 대한 깊은 복사를 시도하는 경우 해결 방법이 있습니다.

간단히 JSON 개체 NSDataNSArray사용한 NSJSONSerialization다음 다시 생성하면 NSArray/NSDictionary새 메모리 참조를 사용하여 완전히 새롭고 새로운 복사본이 생성 됩니다.

그러나 NSArray / NSDictionary의 객체와 그 자식은 JSON 직렬화 가능해야합니다.

NSData *aDataOfSource = [NSJSONSerialization dataWithJSONObject:oldCopy options:NSJSONWritingPrettyPrinted error:nil];
NSDictionary *aDictNewCopy = [NSJSONSerialization JSONObjectWithData:aDataOfSource options:NSJSONReadingMutableLeaves error:nil];

1
NSJSONReadingMutableContainers이 질문에서 사용 사례 를 지정해야 합니다.
Nikolai Ruhe
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.