Objective-C에서 개체를 복사하는 방법


112

자체 개체가있는 사용자 지정 개체를 딥 복사해야합니다. 나는 주위를 읽고 NSCopying을 상속하는 방법과 NSCopyObject를 사용하는 방법에 대해 약간 혼란 스럽습니다.


1
위대한 튜토리얼 이해 복사, mutableCopy와 copyWithZone에 대한
horkavlna

답변:


192

항상 참조 유형과 마찬가지로 "복사"에는 두 가지 개념이 있습니다. 나는 당신이 그들을 알고 있다고 확신하지만 완전성을 위해.

  1. 비트 복사. 여기에서 우리는 비트에 대한 메모리 비트를 복사합니다. 이것이 NSCopyObject가하는 일입니다. 거의 항상, 그것은 당신이 원하는 것이 아닙니다. 개체에는 내부 상태, 기타 개체 등이 있으며 종종 해당 데이터에 대한 참조를 보유하는 유일한 개체라고 가정합니다. 비트 복사는이 가정을 깨뜨립니다.
  2. 깊고 논리적 인 사본. 여기에서 우리는 객체의 사본을 만들지 만 실제로 비트 단위로 수행하지 않고 모든 의도와 목적에 대해 동일하게 작동하지만 (필연적으로) 원본의 메모리와 동일한 복제본이 아닌 객체를 원합니다. Objective C 매뉴얼에서는 이러한 객체를 원본과 "기능적으로 독립적"이라고 부릅니다. 이러한 "지능적인"복사본을 만드는 메커니즘은 클래스마다 다르기 때문에 객체 자체가이를 수행하도록 요청합니다. 이것이 NSCopying 프로토콜입니다.

후자를 원합니다. 이것이 자신의 객체 중 하나라면 NSCopying 프로토콜을 채택하고-(id) copyWithZone : (NSZone *) zone을 구현하기 만하면됩니다. 원하는 것은 무엇이든 자유롭게 할 수 있습니다. 아이디어는 당신 자신의 실제 사본을 만들고 그것을 반환하는 것입니다. 모든 필드에서 copyWithZone을 호출하여 깊은 복사본을 만듭니다. 간단한 예는

@interface YourClass : NSObject <NSCopying> 
{
   SomeOtherObject *obj;
}

// In the implementation
-(id)copyWithZone:(NSZone *)zone
{
  // We'll ignore the zone for now
  YourClass *another = [[YourClass alloc] init];
  another.obj = [obj copyWithZone: zone];

  return another;
}

그러나 당신은 복사 된 객체의 수신자가 그것을 해제 할 책임을지게하고 있습니다! 그렇지 autorelease않나요, 아니면 여기서 뭔가 빠졌나요?
bobobobo

30
@bobobobo : 아니요, Objective-C 메모리 관리의 기본 규칙은 다음과 같습니다. 이름이 "alloc"또는 "new"로 시작하거나 "copy"를 포함하는 메서드를 사용하여 개체를 만들면 개체의 소유권을 갖게됩니다. copyWithZone:이 기준을 충족하므로 보유 수가 +1 인 개체를 반환해야합니다.
Steve Madsen

1
@Adam 영역이 전달 되었기 때문에 alloc대신 사용할 이유가 allocWithZone:있습니까?
Richard

3
음, 영역은 현대 OS X 기반 런타임에서 효과적으로 사용되지 않습니다 (즉, 문자 그대로 사용되지 않는다고 생각합니다). 하지만 예, allocWithZone.
Adam Wright


25

Apple 문서에 따르면

copyWithZone : 메소드의 서브 클래스 버전은 서브 클래스가 NSObject에서 직접 내려 오지 않는 한 구현을 통합하기 위해 먼저 super에 메시지를 보내야합니다.

기존 답변에 추가

@interface YourClass : NSObject <NSCopying> 
{
   SomeOtherObject *obj;
}

// In the implementation
-(id)copyWithZone:(NSZone *)zone
{
  YourClass *another = [super copyWithZone:zone];
  another.obj = [obj copyWithZone: zone];

  return another;
}

2
YourClass가 NSObject의에서 직접 내려 때문에 나는 여기에 필요한 생각하지 않습니다
마이크

2
좋은 지적이지만 긴 클래스 계층 구조의 경우 일반적인 규칙입니다.
Saqib Saud

8
오류가 발생했습니다 : No visible @interface for 'NSObject' declares the selector 'copyWithZone:'. 나는 이것이 우리가 구현하는 다른 사용자 정의 클래스에서 상속 할 때만 필요하다고 생각합니다copyWithZone
Sam

1
another.obj = [[obj copyWithZone : zone] autorelease]; NSObject의 모든 하위 클래스에 대해. 원시 데이터 유형의 경우 할당하면됩니다.-> another.someBOOL = self.someBOOL;
hariszaman

@Sam "NSObject는 자체적으로 NSCopying 프로토콜을 지원하지 않습니다. 서브 클래스는 프로토콜을 지원하고 copyWithZone : 메소드를 구현해야합니다. copyWithZone : 메소드의 서브 클래스 버전은 구현을 통합하기 위해 super에 메시지를 먼저 보내야합니다. NSObject에서. " developer.apple.com/documentation/objectivec/nsobject/…
s4mt6

21

나는 그 코드와 내 코드의 차이점을 모르지만 그 해결책에 문제가있어서 조금 더 읽고 객체를 반환하기 전에 설정해야한다는 것을 알았습니다. 나는 다음과 같은 것을 의미합니다.

#import <Foundation/Foundation.h>

@interface YourObject : NSObject <NSCopying>

@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *line;
@property (strong, nonatomic) NSMutableString *tags;
@property (strong, nonatomic) NSString *htmlSource;
@property (strong, nonatomic) NSMutableString *obj;

-(id) copyWithZone: (NSZone *) zone;

@end


@implementation YourObject


-(id) copyWithZone: (NSZone *) zone
{
    YourObject *copy = [[YourObject allocWithZone: zone] init];

    [copy setNombre: self.name];
    [copy setLinea: self.line];
    [copy setTags: self.tags];
    [copy setHtmlSource: self.htmlSource];

    return copy;
}

이 문제에 많은 문제가 있고 왜 발생하는지에 대한 단서가 없기 때문에이 답변을 추가했습니다. 나는 그 차이를 모르지만 그것은 나를 위해 일하고 어쩌면 다른 사람들에게도 유용 할 수 있습니다 :)


3
another.obj = [obj copyWithZone: zone];

이 줄 obj은 (내가 가정) 선언 된 속성 을 통해 액세스하기 때문에 메모리 누수가 발생 한다고 생각 retain합니다. 따라서 보유 횟수 는 속성 및 copyWithZone.

다음과 같아야한다고 생각합니다.

another.obj = [[obj copyWithZone: zone] autorelease];

또는:

SomeOtherObject *temp = [obj copyWithZone: zone];
another.obj = temp;
[temp release]; 

아니요, 메서드 alloc, copy, mutableCopy, new는 자동 해제되지 않은 개체를 반환해야합니다.
kovpas

@kovpas, 당신은 확실합니까, 당신은 나를 이해하고 있습니까? 반환 된 객체에 대해 말하는 것이 아니라 데이터 필드에 대해 말하는 것입니다.
Szuwar_Jr

네, 미안 해요. 마이너스를 제거 할 수 있도록 어떻게 든 답변을 편집 해 주시겠습니까? :)
kovpas

0

복사를 위해-> 연산자를 사용할 수도 있습니다. 예를 들면 :

-(id)copyWithZone:(NSZone*)zone
{
    MYClass* copy = [MYClass new];
    copy->_property1 = self->_property1;
    ...
    copy->_propertyN = self->_propertyN;
    return copy;
}

여기서 이유는 복사 된 객체가 원래 객체의 상태를 반영해야하기 때문입니다. "." 연산자는 논리를 포함 할 수있는 getter를 호출하므로 부작용이 발생할 수 있습니다.

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