[Effective Objective-C] #5 열거형을 사용해 상태, 옵션, 상태 코드를 정의하라
출처 : Effective Objective-C
-
enum 은 시스템 프레임워크 전반에 걸쳐 광범위하게 사용되지만 개발자들은 잘 활용하지 않는다.
enum 은 예를 들어 에러 상태 코드들이나 조합할 수 있는 옵션들에 사용될 수 있는 상수들을 정의하는 데 매우 유용하게 쓰일 수 있다.
enum EOCConnectionState{
EOCConnectionStateDisconnected,
EOCConnectionStateConnected,
};
-
enum 을 사용하면 코드가 읽기 좋아진다.
컴파일러는 열거형의 각 멤버에 유일한 값을 부여한다.
0으로 시작해 1씩 증가시킨 값을 각 멤버에 부여한다.
이런 열거형 내부의 타입은 컴파일러에 따라 다르다.
-
위의 방법은 사용할 때 다음과 같이 enum 을 붙여 사용해야 해서
enum EOCConnectionState state = EOCConnectionStateDisconnected;
다음과 같이 typedef 를 추가하여 사용한다.
typedef enum EOCConnectionState EOCConnectionState;
-
C++ 11 부터 underlying type(기저 타입)을 기술할 수도 있다.
enum EOCConnectionState : NSInteger{
...
};
기저 타입 기술의 장점은 열거형 타입을 포워드 선언 할 수 있다는 것이다.
컴파일러가 underlying type 이 최종적으로 차지하는 크기를 알 수 없기 떄문에 타입이 사용될 때 컴파일러는 변수를 할당하기 위해 필요한 공간의 크기를 알 수 없다.
enum 의 포워드 선언은 다음과 같이 한다.
enum EOCConnectionState :NSInteger;
-
enum 의 값을 직접 정의하는 것도 가능하다.
enum EOCConnectionState{
EOCConnectionStateDisconnected = 1,
EOCConnectionStateConnected,
};
-
열거형이 정확하게 정의되었으면 비트 연산인 OR 연산자를 이용해 조합될 수 있다.
그리고 AND 비트 연산자를 이용해 특정 옵션값이 켜져 있는지 확인할 수 있다.
-
Foundation 프레임워크에는 열거형 타입을 정의하는 데 도움이 되는 여러 헬퍼가 있다.
또 이 헬퍼들을 이용하면 열거형 타입이 값을 저장할 때 사용하는 내부 타입을 정의할 수 있다.
이러한 헬퍼들은 하위 호환( backward compatibility)을 지원한다.
지정한 컴파일러가 새로운 표준을 지원하면 해당 문법이 그대로 사용되고, 지원되지 않으면 예전 문법으로 돌려준다.
헬퍼는 전처리기 #define 매크로 형태로 제공된다.
NS_ENUM 과 NS_OPTIONS 가 그것이다.
typedef NS_ENUM(NSUInteger, EOCConectionState){
EOCConnectionStateDisconnected,
EOCConnectionStateConnected,
};
typedef NS_OPTIONS(NSUInteger, EOCPermittedDirection){
EOCPermittedDirectionUp = 1 << 0,
EOCPermittedDirectionDown = 1 << 1,
EOCPermittedDirectionLeft = 1 << 2,
EOCPermittedDirectionRight = 1 << 3,
};
-
매크로는 첫 번째로 컴파일러가 새로운 열거형을 지원하는지 여부를 검사한다.
그래서 기능이 없으면 열거형을 예전 방법으로 정의한다.
기능이 있으면 위 예제의 NS_ENUM 은 다음과 같이 converting 된다고 볼 수 있다.
typedef enum EOCConnectionState : NSUInteger EOCConnectionState;
enum EOCConnectionState : NSUInteger {
EOCConnectionStateDisconnected,
EOCConnectionStateConnected,
};
-
NS_OPTIONS 매크로는 C++ 로 컴파일을 하든 하지 않든 여러 가지 방법으로 정의된다.
C++ 가 아니면 그것은 NS_ENUM 과 똑같이 확장되지만, C++ 면 약간 다르게 확장된다.
C++ 컴파일러는 열거형 값 두 개를 OR 비트 연산자로 조합하면 다르게 동작하기 때문이다.
OR 연산으로 조합했을 때 C++ 는 조합 결과가 열거형이 나타내고 있는 타입인 NSUInteger 이기를 기대한다.
또 열거형 타입은 암묵적 캐스팅(implicit cast)을 허용하지 않는다.
EOCPermittedDirection permittedDirections = EOCPermittedDirectionLeft | EOCPermittedDirectionUp;
위 코드를 수행하면, 컴파일러가 C++ 모드였다면 다음과 같은 에러가 나타난다.
error: cannot initialize a variable of type ‘EOCPermittedDirection’ with an rvalue of type ‘int'
OR 연산의 결과에 대해 EOCPermittedDirection 으로 명시적인 캐스팅이 요구된다.
그래서 C++ 을 위한 NS_OPTIONS 열거형은 이런 에러가 발생하지 않게 조금 다른 방법으로 정의된다.
이런 이유 때문에 OR 연산으로 조합할 수 있는 열거형이 필요하면 항상 NS_OPTIONS 를 사용해야 한다.
필요 없다면 NS_ENUM 을 사용하라.
-
enum을 활용하는 마지막 방법은 switch 문장에서 이용하는 것이다.
switch 문에 default 구문을 넣고 싶을 것이다.
그러나 상태 머신(state machine) 을 정의하는 enum을 switch 문에 사용할 때 default 문은 사용하지 않는 게 최선이다.
그 이유는 나중에 열거형에 새로운 상태를 추가했을 때 컴파일러는 새롭게 추가된 상태가 switch 문에서 다루어지지 않는다는,
도움이 되는 경고를 보내주기 때문이다.
default 구문이 새로운 값을 처리하면 컴파일러는 경고를 보내지 않는다.
-
기억할 점
enum 을 사용하여 상태 머신의 상태, 메서드 인자로 쓰이는 옵션, 에러 상태 코드에 사용되는 값에 읽기 좋은 이름을 주라.
enum 타입이 여러 가지 옵션이 동시에 사용될 수 있는 메서드 옵션을 정의한다면, 열거형의 값을 2의 제곱 크기로 선언하여 옵션들이 OR 연산을 할 수 있게 하라.
명시적 타입으로 enum 을 선언하기 위해 NS_ENUM 과 NS_OPTIONS 매크로를 사용하라.
매크로를 사용하면 컴파일러가 선택한 타입이 아닌 직접 선택한 타입이 사용되는 것을 보장한다.
enum 타입을 switch 문에서 사용할 때 default 문은 구현하지 말라.
그렇게 하면 enum에 신규 값을 추가할 때 도움이 된다.
컴파일러는 switch 문이 enum의 모든 값을 다루지 않았을 때 경고를 주기 때문이다.
'프로그래밍 놀이터 > iOS' 카테고리의 다른 글
[Effective Objective-C] #7 인스턴스 변수에 내부에서 접근할 때는 직접 접근하라. (0) | 2017.07.28 |
---|---|
[Effective Objective-C] #6 프로퍼티를 이해하라 (0) | 2017.07.27 |
[Objective-C] Objective-C++ 이란? (0) | 2017.07.25 |
[Effecitve Objective-C] #4 전처리기 #define 보다는 타입이 있는 상수를 사용하라 (0) | 2017.07.24 |
[Objective-C] Constant(상수) 정의하기 (0) | 2017.07.23 |
댓글