[Objective-C] _cmd는 어디서 튀어나왔나.

신비의 키워드랄 것까지는 없고, 최근에 다시 Objective-C 런타임 레퍼런스를 살펴보다 알게된 내용으로 대체함.

사실 더 신비로운 것은 self 키워드인데… 흔히 self키워드가 클래스의 인스턴스 객체를 일컫는다고 생각된다. 결과적으로는 맞는 말이기는한데 Objective-C 런타임의 입장에서보면 self는 “메시지를 수신하는 객체”의 의미가 강하다. 기술적으로 Objective-C에서 인스턴스 객체는 메소드에 대한 소유권이 없다.1 모든 메소드는 클래스가 가지고 있다.

즉 self는 객체의 메소드 내에서만 유효한 키워드이고, 이는 해당 메소드가 호출되었을 때(즉 메시지를 받았을 때) 메시지를 받은 target 객체를 의미한다.

런타임은 객체가 메시지를 받으면 (메소드를 호출하면) 해당 메소드를 실행하는 것을 함수 프로시저 호출로 처리하게 된다. (컴파일러에 의해 메소드들은 따로 함수로 만들어져서 메모리 어딘가에 로드되어 있다.)

이 함수 프로시저의 호출은 필수적으로 두 가지 파라미터를 받는데, 하는 메시지를 받은 객체 즉 self이고 다른 하나는 객체가 받은 메시지의 정보를 담는 객체인 셀렉터이다.

셀렉터는 메소드에 대한 정보를 담고 있는 객체인데 (메소드 != 셀렉터) 메소드의 이름을 숫자값에 맵핑해 놓은 것이라 보면 된다. 이 “호출된 메소드를 나타내는 셀렉터”를 가리키는 변수가 _cmd이며, 이것은 self와 마찬가지로 모든 메소드에서 사전에 미리 선언되고 정의돼 있는 것처럼 접근할 수 있다.

예를 들어 NSStringFromSelector(SEL selector)함수는 Objective-C 런타임이 제공하는데, 전달받은 셀렉터로부터 다시 메소드의 이름을 복원하여 문자열 객체로 돌려준다.

SEL 타입의 선언은 objc.h 에 있으며 typedef struct objc_selector *SEL;로 정의되어 있다.

관련하여 추가 포스팅이 있을 예정임.

말 그대로 신비의 메소드인 _cmd. 이름만으로 유추해보건데, 뭔가 “command”와 관련있는 backing store 변수이름인 것 같다. 빙고, _cmd는 현재 메소드의 셀렉터이름을 저장하는 변수이다.

이걸 문자열로 확인하려면 NSStringFromSelector(_cmd) 라고 쓴다. 아래의 일련의 코드를 보자.

-(BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)options
{
NSLod(@"%@",NSStringFromSelector(_cmd));
    return YES;
}

-(void)applicationDidEnterBackground:(UIApplication*)application
{
  NSLog(@"%@",NSStringFromSelector(_cmd));
}

-(void)applicationWillEnterForeground:(UIApplication*)application
{
    NSLog(@"%@",NSStringFromSelector(_cmd));
}

-(void)applicationDidBecomeActive:(UIApplication*)application {
    NSLog(@"%@",NSStringFromSelector(_cmd));
}

-(void)applicationWillTerminate:(UIApplication*)application {
    NSLog(@"%@",NSStringFromSelector(_cmd));
}

실행되면, 예상할 수 있듯이 아래와 비슷한 모양으로 로그가 찍힌다.

application:didFinishedLaunchingWithOptions:
applicationWillTerminate:

NSFetchedResultsController 관련 자료를 살펴보다, 눈에 띄었던 “마법의 코드”조각이었음.


  1. 객체가 메시지를 받으면 해당 객체는 isa 포인터가 가리키고 있는 클래스로부터 해당 메시지의 셀렉터를 찾고, 그 셀렉터가 가리키고 있는 함수 포인터를 호출한다. 이때, 어떤 인스턴스가 메시지를 받았는지를 구분하기 위해서 C함수들은(모든 메소드는 다시 C함수의 형태로 변환되어 존재한다) 항상 첫번째 인자로 메시지를 받은 target 객체를 받도록 변형되어 있다.