NSString 속성 : 복사 또는 유지?


331

하자 내가라는 클래스가 있다고 가정 SomeClassstring속성 이름을 :

@interface SomeClass : NSObject
{
    NSString* name;
}

@property (nonatomic, retain) NSString* name;

@end

본인은 이름에 할당 될 수 NSMutableString있으며이 경우 잘못된 행동으로 이어질 수 있음을 이해합니다 .

  • 일반적으로 문자열의 경우 항상copy 대신 속성 을 사용하는 것이 좋습니다 retain.
  • "복사 된"속성이 "유지 된"속성보다 덜 효율적입니까?

6
후속 질문 : name출시 dealloc여부는?
Chetan

7
@chetan 그래!
Jon

답변:


440

NSCopying프로토콜 을 준수하는 불변 값 클래스 인 속성의 경우 거의 항상 선언에 지정해야 copy합니다 @property. retain이러한 상황에서 지정 은 거의 원하지 않습니다.

그 이유는 다음과 같습니다.

NSMutableString *someName = [NSMutableString stringWithString:@"Chris"];

Person *p = [[[Person alloc] init] autorelease];
p.name = someName;

[someName setString:@"Debajit"];

의 현재 값 Person.name속성은 속성이 선언되는지 여부에 따라 상이 할 것이다 retain또는 copy그것이 될 것이다 - @"Debajit"속성이 표시되어있는 경우 retain만, @"Chris"속성이 표시되어있는 경우 copy.

거의 모든 경우 에 객체의 속성을 뒤에서 변경 하지 않으려면 객체를 나타내는 속성을 표시해야합니다 copy. (그리고 사용하는 대신 세터를 직접 작성하는 경우 @synthesize실제로 copy대신 사용 하는 것을 기억해야 retain합니다.)


61
이 답변은 약간의 혼란을 초래할 수 있습니다 ( robnapier.net/blog/implementing-nscopying-439#comment-1312 참조 ). 당신은 NSString에 대해 절대적으로 맞지만, 당신이 요점을 너무 일반적이라고 생각합니다. NSString을 복사해야하는 이유는 공통 변경 가능한 서브 클래스 (NSMutableString)를 가지고 있기 때문입니다. 변경 가능한 서브 클래스가없는 클래스 (특히 직접 작성하는 클래스)의 경우 시간과 메모리 낭비를 피하기 위해 복사보다는 유지하는 것이 좋습니다.
Rob Napier

63
당신의 추론이 잘못되었습니다. 시간 / 메모리를 기준으로 복사 또는 유지할지 여부를 결정하지 말고 원하는 의미를 기반으로 결정해야합니다. 그렇기 때문에 필자가 구체적으로 "불변 값 클래스"라는 용어를 사용했습니다. 또한 클래스에 변경 가능한 서브 클래스가 있는지 또는 변경 가능한 자체인지 여부는 중요하지 않습니다.
Chris Hanson

10
Obj-C가 유형별로 불변성을 강제 할 수 없다는 것은 부끄러운 일입니다. 이것은 C ++의 전이 const가 부족한 것과 같습니다. 개인적으로 나는 문자열이 항상 불변 인 것처럼 작동 합니다. 변경 가능한 문자열을 사용해야하는 경우 나중에 변경해도 변경 불가능한 참조를 전달하지 않습니다. 코드 냄새가 아닌 다른 것을 고려할 것입니다. 결과적으로-혼자서 작업하는 코드에서 모든 문자열을 유지합니다. 팀의 일원으로 일하는 경우 상황이 다르게 보일 수 있습니다.
philsquared

5
@ 필 내쉬 : 나는 혼자서 작업하는 프로젝트와 다른 사람들과 공유하는 프로젝트에 다른 스타일을 사용하는 것이 코드 냄새라고 생각합니다. 모든 언어 / 프레임 워크에는 개발자가 동의하는 일반적인 규칙 또는 스타일이 있습니다. 개인 프로젝트에서 무시하는 것은 잘못된 것 같습니다. 그리고 당신의 이론적 근거를 위해 "내 코드에서, 변경 가능한 문자열을 반환하지 않습니다": 그것은 당신의 자신의 문자열에는 효과가 있을지 모르지만, 당신은 프레임 워크로부터받는 문자열에 대해서는 전혀 알지 못합니다.
Nikolai Ruhe

7
@Nikolai 나는 NSMutableString일시적인 "문자열 빌더"유형을 제외하고는을 사용하지 않는다 . 나는 그것들을 신중한 유형으로 선호하지만 원문 문자열을 변경할 수없는 경우 사본이 자유롭게 보존 할 수 있다는 사실이 내 관심의 대부분을 완화시킬 수 있습니다.
philsquared

120

NSString에는 복사를 사용해야합니다. Mutable이면 복사됩니다. 그렇지 않은 경우 유지됩니다. 정확히 앱에서 원하는 의미론 (유형이 최선을 다하도록하십시오).


1
나는 여전히 변경 가능하고 변경 불가능한 형식을 신중하게 선호하지만 원래 문자열이 변경 불가능한 경우 사본이 유지 될 수 있기 전에는 몰랐습니다. 감사.
philsquared

25
어쨌든 얻을 NSString것으로 선언 된 속성 을 언급하면 ​​+1 (물론 불변 인 경우). 내가 생각할 수있는 다른 예는 입니다. copyretainNSNumber
matm

이 답변과 @GBY가 투표 한 투표의 차이점은 무엇입니까?
게리 린

67

일반적으로 문자열의 경우 항상 retain 대신 copy 속성을 사용하는 것이 좋습니다?

예-일반적으로 항상 복사 속성을 사용하십시오.

NSString 속성NSString 인스턴스 또는 NSMutableString 인스턴스를 전달할 수 있기 때문에 전달되는 값이 불변 또는 변경 가능한 객체인지 실제로 확인할 수 없기 때문입니다.

"복사 된"속성이 "유지 된"속성보다 덜 효율적입니까?

  • 속성에 NSString 인스턴스 가 전달 되면 대답은 " 아니오 "입니다. 복사는 유지하는 것보다 덜 효율적입니다.
    NSString은 실제로 복사를 수행 할 수 없을만큼 똑똑하기 때문에 효율성이 떨어집니다.

  • 속성에 NSMutableString 인스턴스 가 전달 되면 대답은 " "입니다. 복사가 보유보다 효율적이지 않습니다.
    (실제 메모리 할당 및 복사가 발생해야하기 때문에 효율성이 떨어지지 만 이는 바람직한 것입니다.)

  • 일반적으로 "복사 된"속성은 효율성이 떨어질 가능성이 있지만 NSCopying프로토콜을 사용 하면 유지하는 것처럼 "복사하는만큼"효율적인 클래스를 구현할 수 있습니다. NSString 인스턴스 가 이에 대한 예입니다.

일반적으로 (NSString뿐만 아니라) "retain"대신 "copy"를 언제 사용해야합니까?

copy속성의 내부 상태가 경고없이 변경되지 않도록하려면 항상 사용해야 합니다. 불변 객체의 경우에도 올바르게 작성된 불변 객체는 복사를 효율적으로 처리합니다 (불변성과 및 다음 섹션 참조 NSCopying).

retain객체에 성능상의 이유가있을 수 있지만 유지 보수 오버 헤드가 있습니다. 코드 외부에서 내부 상태가 변경 될 가능성을 관리해야합니다. 그들이 말한대로-마지막으로 최적화하십시오.

그러나 나는 나의 수업을 불변으로 썼다 – 단지 그것을 "유지"할 수 없는가?

사용 안함 copy. 클래스가 실제로 변경 불가능한 경우, 사용될 NSCopying때 클래스 자체를 반환하도록 프로토콜을 구현하는 것이 가장 좋습니다 copy. 이렇게하면 :

  • 수업의 다른 사용자는 사용할 때 성능 이점을 얻을 수 있습니다 copy.
  • copy주석은 자신의 코드를 유지 관리하기 쉽게 해줍니다 . 주석은 copy이 객체가 다른 곳에서이 객체의 상태 변경에 대해 걱정할 필요가 없음을 나타냅니다.

39

이 간단한 규칙을 따르려고합니다.

  • 객체 속성을 할당 할 때 객체 의 을 유지하고 싶 습니까? copy를 사용 하십시오 .

  • 나는 붙잡고 싶어 개체나는 그것의 내부 값은 어떤 상관 없어 현재 또는 미래에있을 것인가? 강력하게 사용하십시오 .

예를 들면 : "Lisa Miller"( 사본 ) 라는 이름 을 붙잡고 싶거나 Lisa Miller ( 강력한 ) 사람 을 붙잡고 싶습니다 . 그녀의 이름은 나중에 "Lisa Smith"로 변경 될 수 있지만 여전히 같은 사람이 될 것입니다.


14

이 예제를 통해 복사 및 유지는 다음과 같이 설명 될 수 있습니다.

NSMutableString *someName = [NSMutableString stringWithString:@"Chris"];

Person *p = [[[Person alloc] init] autorelease];
p.name = someName;

[someName setString:@"Debajit"];

속성이 copy 유형 인 경우,

[Person name]문자열의 내용을 보유 할 문자열에 대해 새 사본이 작성 됩니다 someName. 이제 someName문자열 에 대한 작업은 영향을 미치지 않습니다 [Person name].

[Person name]someName문자열은 서로 다른 메모리 주소를해야합니다.

그러나 유지하는 경우

둘 다 [Person name]somename 문자열과 동일한 메모리 주소를 보유하며 somename 문자열의 유지 횟수는 1 씩 증가합니다.

따라서 somename 문자열의 변경 사항은 문자열에 반영됩니다 [Person name].


3

속성 선언에 '복사'를 넣는 것은 힙의 객체가 참조로 전달되는 객체 지향 환경을 사용하여 비행하는 것입니다. 여기서 얻는 이점 중 하나는 객체를 변경할 때 해당 객체에 대한 모든 참조입니다 최신 변경 사항을 참조하십시오. 많은 언어가 'ref'또는 유사한 키워드를 제공하여 값 유형 (예 : 스택의 구조)이 동일한 동작의 이점을 얻을 수 있도록합니다. 개인적으로 필자는 복사를 거의 사용하지 않고 할당 된 객체의 변경 사항으로부터 속성 값을 보호해야한다고 생각되면 할당하는 동안 해당 객체의 복사 방법을 호출 할 수 있습니다. 예 :

p.name = [someName copy];

물론 해당 속성이 포함 된 객체를 디자인 할 때 할당이 복사되는 패턴에서 디자인이 이점을 얻는 지 여부 만 알 수 있습니다. Cocoawithlove.com 은 다음과 같이 말합니다.

"세터 매개 변수를 변경할 수 있지만 경고없이 속성의 내부 상태를 변경할 수없는 경우 복사 접근 자를 사용해야합니다."-예기치 않은 변경 값을 참을 수 있는지 여부에 대한 판단은 모두 본인의 것입니다. 이 시나리오를 상상해보십시오.

//person object has details of an individual you're assigning to a contact list.

Contact *contact = [[[Contact alloc] init] autorelease];
contact.name = person.name;

//person changes name
[[person name] setString:@"new name"];
//now both person.name and contact.name are in sync.

이 경우 복사를 사용하지 않고 접점 오브젝트는 새 값을 자동으로 가져옵니다. 그러나 우리가 그것을 사용했다면, 변경 사항을 감지하고 동기화했는지 수동으로 확인해야합니다. 이 경우 의미를 유지하는 것이 바람직 할 수 있습니다. 다른 경우에는 복사가 더 적합 할 수 있습니다.


1
@interface TTItem : NSObject    
@property (nonatomic, copy) NSString *name;
@end

{
    TTItem *item = [[TTItem alloc] init];    
    NSString *test1 = [NSString stringWithFormat:@"%d / %@", 1, @"Go go go"];  
    item.name = test1;  
    NSLog(@"-item.name: point = %p, content = %@; test1 = %p", item.name, item.name, test1);  
    test1 = [NSString stringWithFormat:@"%d / %@", 2, @"Back back back"];  
    NSLog(@"+item.name: point = %p, content = %@, test1 = %p", item.name, item.name, test1);
}

Log:  
    -item.name: point = 0x9a805a0, content = 1 / Go go go; test1 = 0x9a805a0  
    +item.name: point = 0x9a805a0, content = 1 / Go go go, test1 = 0x9a84660

0

NSString 속성을 선언하려면 항상 copy 를 사용해야 합니다.

@property (nonatomic, copy) NSString* name;

변경 불가능한 문자열을 리턴하는지 (변경 가능 문자열이 전달 된 경우) 또는 보유 문자열을 리턴하는지 (불변 문자열이 전달 된 경우)에 대한 자세한 정보는이 내용을 읽어야합니다.

NSCopying 프로토콜 참조

클래스와 그 내용을 변경할 수 없을 때 새 복사본을 만드는 대신 원본을 유지하여 NSCopying을 구현하십시오.

가치 객체

따라서 변경 불가능한 버전의 경우 다음과 같이하면됩니다.

- (id)copyWithZone:(NSZone *)zone
{
    return self;
}

-1

name은 (불변) NSString이므로 다른 NSString것을 name으로 설정하면 복사 또는 유지에 차이가 없습니다 . 다시 말해, copy는 유지와 같이 동작하여 참조 횟수를 1 씩 증가시킵니다. 나는 그것이 불변이고 복제 할 필요가 없기 때문에 불변의 클래스에 대한 자동 최적화라고 생각합니다. 그러나 a NSMutalbeString mstr를 이름으로 설정하면 mstr정확성을 기하기 위해 내용 이 복사됩니다.


1
선언 된 유형을 실제 유형과 혼동하고 있습니다. "retain"특성을 사용하고 NSMutableString을 지정하면 해당 NSMutableString은 유지되지만 여전히 수정할 수 있습니다. "복사"를 사용하면 NSMutableString을 할당 할 때 변경 불가능한 사본이 작성됩니다. 그때부터 변경 가능한 문자열의 복사본 자체가 변경 불가능하기 때문에 속성의 "복사"에서 그대로 유지됩니다.
gnasher729

1
보유 변수에서 온 객체를 사용하는 경우 해당 변수가 수정 될 때 객체도 복사됩니다. 객체가 복사 된 변수에서 온 경우 객체의 현재 값은 다음과 같습니다. 변경되지 않음
Bryan P

-1

문자열이 매우 큰 경우 복사는 성능에 영향을 미치며 큰 문자열의 두 사본은 더 많은 메모리를 사용합니다.

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