KVO 컨텍스트 표현을 위한 자기참조 포인터

다음과같은 포인터변수에 자신의 변수명 그대로 자기자신의 주소값을 할당하는 특이한 변수선언을 처음 볼 경우 당황스러울 것이다.

void *myVariableName = &myVariableName

하지만 이는 자기참조 포인터 혹은 유니크 컨텍스트 포인터(unique context pointer)라는 이름으로 불리는 엄연히 C/C++/Obj-C에서 유효한 문법이다. 이를 이용하여 컴파일타임에 유니크한 포인터값을 생성할 수 있어서 컨텍스트 구분을 위한 변수를 표현할때 매우 유용하게 사용될 수 있다.

해당 컨텍스트변수가 해당 파일 내에서만 사용할 경우.

.m파일에 다음과같이 선언하면된다.

static void *MyContext = &MyContext;

만약 다른 파일에서도 사용되는 컨텍스트라면,

.h 파일에

extern void *MyContext;

.m 파일에

void *MyContext = &MyContext;

이러한 변수범위에 관한 내용은 C/C++에서 auto,static,extern 키워드의 의미 포스팅에 더 자세히 적어두었다.

특정 컨텍스트를 나타내기 위해 포인터를 이용하는경우 아래와같이 NSString 객체에 유니크 키를 넣어서 const 로 사용하는 경우도 많이 있다.

const NSString* MyContext = @“unique_key_my_context”;

하지만 string의 특성을 이용하여 string 자체의 값을 이용하는 추가 로직이 들어가지 않는다면, 자기참조 포인터변수를 이용하는것이 메모리 사용측면이나, 컨텍스트 비교시 속도 성능면에서도 더 좋고 코딩하기도 간편하다.

다음은 애플에서 제공하는 AVPlayerDemo 샘플프로젝트에서 자기참조 포인터로 KVO(Key-Value Observing)에 사용되는 컨텍스트를 구현한 형태이니 참고해보자.

static void *AVPlayerDemoPlaybackViewControllerRateObservationContext = &AVPlayerDemoPlaybackViewControllerRateObservationContext;
static void *AVPlayerDemoPlaybackViewControllerStatusObservationContext = &AVPlayerDemoPlaybackViewControllerStatusObservationContext;
static void *AVPlayerDemoPlaybackViewControllerCurrentItemObservationContext = &AVPlayerDemoPlaybackViewControllerCurrentItemObservationContext;


// 옵저버 등록
- (id)init {
    ...
   [self.mPlayerItem addObserver:self 
                      forKeyPath:kStatusKey 
                         options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                        context:AVPlayerDemoPlaybackViewControllerStatusObservationContext];
    ...
}

// 옵저버 변화 감시지 해당 컨텍스트에 따라 다르게 동작
- (void)observeValueForKeyPath:(NSString*) path
        ofObject:(id)object
        change:(NSDictionary*)change
        context:(void*)context
{
    /* AVPlayerItem "status" property value observer. */
    if (context == AVPlayerDemoPlaybackViewControllerStatusObservationContext)
    {
        [self syncPlayPauseButtons];
    }
    else if(context == AVPlayerDemoPlaybackViewControllerRateObservationContext) {
        ...
    }
    ...
}