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

[Effective Objective-C] #18 가변 객체보다는 불변 객체를 사용하라

by 돼지왕 왕돼지 2017. 8. 21.
반응형

 [Effective Objective-C] #18 가변 객체보다는 불변 객체를 사용하라


출처 : Effective Objective-C

API, inconsistent, iskindofclass, key-value coding, KVC, Mutable, nonatomic, NSMutableSet, NSSet, read-write, readonly, readwrite 재선언, setidentifier:, setValue:forkey:, synchronized, [Effective Objective-C] #18 가변 객체보다는 불변 객체를 사용하라, 가변 객체, 가변 집합, 가변적, 동기화, 디스패치 큐, 메모리 관리 시맨틱, 메모리 시맨틱, 메모리 주소, 복사 비용, 불변 객체, 불변 집합 복사본, 불일치, 읽기 쓰기, 읽기 전용, 추천, 컴파일러, 컴파일러 에러, 클래스 확장 카테고리, 키 값 코딩, 퍼블릭 세터, 프로퍼티, 프로퍼티 속성, 해킹


-

프로퍼티를 기본 설정으로 두면 읽기 쓰기(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)로 만들라.


프로퍼티를 내부에서 설정할 수 있게 하려면 클래스 확장 카테고리로 읽기 전용 속성을 읽기 쓰기로 확장하라.


가변 컬렉션을 프로퍼티로 노출하기보다는 컬렉션에 포함된 객체를 조작할 수 있는 메서드를 제공하라.




반응형

댓글