[Effective Objective-C] #18 가변 객체보다는 불변 객체를 사용하라
출처 : Effective Objective-C
-
프로퍼티를 기본 설정으로 두면 읽기 쓰기(read-write) 인데 이는 클래스를 가변적(mutable)로 만든다.
-
컬렉션이 가변 객체를 저장하고 집합(컬렉션)에 포함된 객체가 변형된다면, 집합의 내부 데이터 구조는 쉽게 불일치(inconsistent) 상태가 될 것이다.
객체는 필요할 때만 가변적으로 만드는 것을 추천한다.
-
불변 객체를 만든다는 것은 프로퍼티를 외부에 읽기 전용으로 만든 다음 필요할 때만 데이터를 노출하는 것을 의미한다.
-
클래스를 불변으로 만들려면 모든 프로퍼티에 readonly 속성을 추가하면 된다.
이제 무엇이든 프로퍼티 값을 변경하려 하면 컴파일러가 에러를 일으킬 것이다.
이 객체를 사용하는 사람은 이 객체의 데이터가 내부에서 변경되지 않는다고 안심할 수 있다.
-
프로퍼티 속성에 메모리 관리 시맨틱을 남겨놓는 것이 좋다.
프로퍼티는 세터를 가질 수 없어 ( 읽기 적용이기 때문에 사실 명시할 필요는 없다. 메모리 관리 시맨틱은 값을 쓸 때만 연관이 있다. ) 그냥 메모리 관리 시맨틱을 기본값으로 놔둘 수도 있다.
그러나 프로퍼티를 선언할 때 여러분이 사용하는 메모리 관리 시맨틱을 설정해 놓으면(필요 없을지라도) 문서화에 도움이 된다.
그리고 나중에 프로퍼티를 읽기-쓰기로 바꿀 때 좀 더 쉽게 할 수 있다.
-
객체 내부에 캡슐화되어 있는 데이터를 외부에서는 변경할 수 없지만,
내부에서는 변경할 수 있게 하고 싶을 수 있다.
이런 경우 할 수 있는 일반적인 방법은 readonly 를 readwrite 로 내부적으로 재선언하는 것이다.
물론 프로퍼티가 nonatomic 이면 경쟁 상태가 될 가능성이 있다.
이는 객체 내부적으로 프로퍼티를 쓰고 있을 때 관찰자(그 객체를 외부에서 쓰는 주체)가 그 프로퍼티를 읽을 수 있게 한다.
이 문제는 내부에서 접근하는 것을 포함하여 프로퍼티에 대한 모든 접근을 동기화(synchronized) 함으로써 해결할 수 있다.
동기화하기 위해 필요하다면 디스패치 큐를 사용하면 된다.
-
프로퍼티 속성을 내부적으로 readwrite 로 재선언하는 것은 클래스 확장 카테고리를 이용해 할 수 있다.
이는 퍼블릭 인터페이스에 있는 프로퍼티를 똑같이 선언하고 readonly 를 readwrite 로 바꾸고 나머지 속성은 똑같이 두는 방법으로 할 수 있다.
-
여전히 객체 외부에서 키-값 코딩(key-value coding: KVC)을 이용해 이 객체의 프로퍼티를 설정할 수 있다.
다시 말해 setValue:forKey: 를 사용해 값을 설정할 수 있다.
KVC 는 퍼블릭 인터페이스에 setIdentifier: 메서드를 공개하지 않더라도 ( 다시 말해 read-only 프로퍼티이기 때문에 ) 볼 수 있는 능력이 있기 때문이다.
-
KVC 를 사용해 read-only 프로퍼티를 설정하는 행위는 클래스의 API 를 침범하는 것처럼 보인다.
그리고 이러한 해킹으로 무언가 잘못될 수 있다면 그러한 가능성을 차단하는 것은 개발자에게 달려 있다.
-
악랄한 개발자는 내성을 이용해 인스턴스가 저장되어 있는 메모리 주소를 알아내어 세터를 사용하지 않고도 프로퍼티를 다룰 수 있다.
이 방법을 이용해 수동으로 인스턴스 값을 설정할 수 있다.
-
퍼블릭 세터가 없더라도 기술적으로는 프로퍼티를 헤집고 다닐 수 있기 때문에 객체를 불변으로 만들라는 조언을 무시하면 안된다.
-
마음속에 새겨두어야 할 또 한 가지는 여러분 클래스의 퍼블릭 API 를 정의할 때 컬렉션 클래스(collection-class) 프로퍼티를 가변으로 할지 불변으로 할지에 대한 것이다.
-
집합을 외부로 노출하는 일반적인 방법은 내부 가변 집합의 불변 집합 복사본을 만들고
그 복사본을 반환하는 readonly 속성의 프로퍼티를 만드는 것이다.
-
개발자가 내부 가변 집합의 복사본을 전달하지 않고 가변 집합 자체를 반환하도록 구현할 수 있다.
이는 집합의 크기가 매우 크다면 적절하고 의도된 것일 수 있다.
이 같은 경우 복사본을 만드는 비용이 크기 때문이다.
NSMutableSet 을 반환하는 것은 허용된다.
이 클래스는 NSSet 의 하위 클래스이기 때문이다.
사용자는 isKindOfClass: 와 같이 NSMutableSet 인지, NSSet 인지 확인하여 조작을 추가하는 코드를 피해야 한다.
위와 같이 의도적으로 불변을 return 해야 하는데 가변을 return 하는 케이스들이 있을 수 있기 때문이다.
기억할 점
가능하다면 객체를 불변(immutable)로 만들라.
프로퍼티를 내부에서 설정할 수 있게 하려면 클래스 확장 카테고리로 읽기 전용 속성을 읽기 쓰기로 확장하라.
가변 컬렉션을 프로퍼티로 노출하기보다는 컬렉션에 포함된 객체를 조작할 수 있는 메서드를 제공하라.
'프로그래밍 놀이터 > iOS' 카테고리의 다른 글
[Effective Objective-C] #20 프라이빗 메서드 이름에 접두어를 사용하라 (0) | 2017.08.31 |
---|---|
[Effective Objective-C] #19 명확하고 일관된 작명법을 사용하라 (0) | 2017.08.30 |
[Effective Objective-C] #17 description 메서드를 구현하라 (0) | 2017.08.20 |
[Effective Objective-C] 목차와 요약을 통해 한 눈에 알아보는 Effective Objective-C #9 ~ #16 (0) | 2017.08.19 |
[Effective Objective-C] #16 지정 초기화 메서드를 만들라 (0) | 2017.08.18 |
댓글