본문 바로가기
프로그래밍 놀이터/iOS

[Effective Objective-C] #22 NSCopying 프로토콜을 이해하라.

by 돼지왕 왕돼지 2017. 9. 2.
반응형

 [Effective Objective-C] #22 NSCopying 프로토콜을 이해하라.


출처 : Effective Objective-C

allocWithZone, COPY, copy 헬퍼, copyWithZone, deep copy, Foundation, immutablecopy, mutablecopy, mutablecopy 헬퍼, mutablecopywithzone, NSArray, nscopying, NSMutableArray, nsmutablecopying, nsobject, NSSet, nszone, protocol, shallow copy, zone 파라미터, [Effective Objective-C] #22 NSCopying 프로토콜을 이해하라., 가변, 기본 존, 깊은 복사, 내부 변수 접근, 내성, 단일 존, 대다수 얕은 복사, 불변, 얕은 복사, 지정 초기화 메서드, 컨테이너, 컬렉션 클래스 얕은 복사, 코드 복잡, 프로토콜


-

오브젝티브-C 에서는 copy 메서드를 이용하여 객체를 복사할 수 있다.

자신이 만든 클래스가 복사될 수 있게 하려면 메서드가 하나뿐인 NSCopying 프로토콜을 구현하면 된다.

- (id)copyWithZone:(NSZone*)zone



-

모든 앱이 단일 존(기본 존)을 가진다.

그래서 이 메서드를 구현할 때 zone 파라미터는 무시해도 된다.



-

copy 메서드는 NSObject 내에서 구현되어 있다.

그리고 기본 존을 이용해 copyWithZone: 을 호출한다.

그냥 copy 를 재정의하고 싶겠지만 꼭 copyWithZone: 을 대신 구현해야 한다.

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

     EOCPerson *copy = [[[self class] allocWithZone:zone] initWithFirstName:_firstName andLastName:_lastName];

     copy->_friends = [_friends mutableCopy]; // 내부용 변수는 -> 로 접근한다.

     return copy;

}


-

대부분 복사본을 초기화하기 위해 지정 초기화 메서드를 사용할 것이다.

그러나 지정 초기화 메서드가 금방 재정의되기 때문에 정의할 필요가 없는 복잡한 구조의 인스턴스 변수를 초기화하는 것 같은 부작용이 있다면 사용하고 싶지 않을 것이다.



-

NSMutableCopying 이라는 또 다른 프로토콜 메서드가 있다.

이 프로토콜은 NSCopying 과 비슷하지만 메서드가 다음과 같이 다르다.

- (id)mutableCopyWithZone:(NSZone*)zone


mutableCopy 헬퍼는 copy 헬퍼와 비슷하다.

그리고 기본 존을 이용해 위 메서드를 호출한다.

클래스가 가변 또는 불변일 수도 있다면 NSMutableCopyting 도 구현해야 한다.

이 패턴을 사용할 때 가변 클래스에서 가변 복사본을 반환하는 copyWithZone: 을 재정의해서는 안 된다.

대신 가변 또는 불변 인스턴스에서 가변 복사본이 필요하면 mutableCopy 를 이용해야 한다.

비슷하게 불변 복사본이 필요하면 copy 를 이용해야 한다.



-

다음 두 메서드 호출은 모두 정상이다.

-[NSMutableArray copy] // => NSArray

-[NSArray mutableCopy] // => NSMutableArray



-

가변 객체에 copy 를 호출하여 반환받은 복사본 객체를 다른 클래스의 인스턴스로 전달할 때는 주의해야 한다.

이렇게 하면 가변 객체를 쉽게 불변 객체로 변환할 수 있다.

동일한 효과를 내는 또 다른 방법은 copy, immutableCopy, mutableCopy 메서드가 모두 동일한 클래스를 반환하게 하는 것이다.

copy 는 항상 같은 클래스를 반환하지만 다른 두 메서드는 상황에 따라 다른 것을 반환한다.

그러나 인스턴스가 가변인지 불변인지 모른다면 이 방법은 좋지 않은 방법이다.

사실은 NSMutableArray 이지만 NSArray 타입으로 반환받은 객체한테 copy 를 호출할 수 있다.

그 경우 반환되는 객체가 불변 객체라고 생각하겠지만 ( copy 는 복사본을 불변 객체로 반환하기 때문에 ) 사실은 가변객체이다.



-

인스턴스 타입을 알아내기 위해 내성을 사용할 수 있지만 복사를 하는 코드가 복잡해질 것이다.

그래서 안전하게 immutableCopy 나 mutableCopy 를 사용하게 될 것이다.

이는 동일한 일을 하는 copy 와 mutableCopy 두 개의 메서드를 사용하는 상황으로 돌아간다.

immutableCopy 대신 copy 를 호출하는 것의 장점은 NSCopying 이 가변, 불변 객체 구분없이 사용할 수 있게 설게되었다는 것이다.

그래서 immutableCopy 의 이름은 잘못 지어진 것일 수도 있다.



-

복사 메서드를 구현할 때 고려해야 할 또 한 가지는 깊은 복사를 할지, 아니면 얕은 복사를 할지 정하는 것이다.

깊은 복사는 객체 내부의 모든 인스턴스 객체를 복사한다.

Foundation 프레임워크의 모든 컬렉션 클래스는 얕은 복사를 한다.

오직 컨테이너만 복사된다.

컨테이너 내부의 객체들은 복사되지 않는다.

얕은 복사를 하는 이유는 컨테이너 내부 객체들이 보통 복사할 수 없는 것들이기 때문이다.

또 일반적으로 모든 내부 객체를 복사하길 원하지 않는다.



-

NSSet 의 경우 다음과 같은 초기화 메서드를 제공한다.

- (id)initWithSet:(NSArray*)array copyItems:(BOOL)copyItems


copyItems 를 YES 로 설정하면 array 의 아이템들한테 copy 메시지를 보내 아이템의 복사본으로 구성된 집합을 생성할 것이다.



-

깊은 복사를 정의하는 프로토콜은 없다.

그래서 각 클래스에서 깊은 복사를 하는 방법을 정의해야 한다.

깊은 복사 메서드가 필요한지 결정할 필요가 있다.

또 절대로 NSCopying 을 따르는 객체가 깊은 복사를 할 거라고 가정하면 안 된다.

거의 대다수 클래스는 얕은 복사를 한다.

어떤 객체에 대해 깊은 복사를 할 필요가 있으면 관련된 메서드를 찾아보거나, 문서에 NSCopying 구현이 깊은 복사를 하지 않는다고 되어 있으면 스스로 구현해야 한다.




기억할 점


객체를 복사할 필요가 있으면 NSCopying 프로토콜을 구현하라.


자신의 객체가 가변, 불변 둘 다 될 수 있으면 NSCopying 과 NSMutableCopying 프로토콜 둘 다 구현하라.


복사가 얕은 복사인지 깊은 복사인지 결정하라.

그리고 가능하다면 얕은 복사를 하라.


자신이 만든 객체가 깊은 복사가 필요하다면 깊은 복사 메서드를 추가하라.




반응형

댓글