Apple🍎/Objective-C

Objective-C의 Selector와 Dispatch Table

생각 깎는 아이 2025. 5. 21. 22:12

Selector의 개념과 역할

Selector는 Objective-C 메시지 전달 시스템의 핵심 요소로, 간단히 말해 메서드의 이름을 식별하는 타입입니다. 기술적으로는 SEL 타입으로 표현되며, 내부적으로는 C 문자열에 대한 특별한 포인터로 구현되어 있습니다.

셀렉터의 가장 중요한 특성은 그것이 단순한 문자열이 아니라, 런타임 시스템에 의해 관리되는 고유한 식별자라는 점입니다. 이 고유성은 메서드 검색 과정을 매우 효율적으로 만들어 줍니다.

Selector의 핵심 특성

  1. 메서드 식별자: Selector는 메서드의 이름과 매개변수 레이블을 포함한 식별자입니다.
  2. 문자열 기반: 내부적으로는 문자열이지만, 런타임 시스템에 의해 관리되는 고유한 식별자로 변환됩니다.
  3. 타입 정보 없음: Selector는 매개변수 타입이나 반환 타입을 포함하지 않고, 오직 이름만 저장합니다.
  4. 메서드 시그니처: Objective-C에서 완전한 메서드 시그니처는 다음과 같습니다
- (ReturnType)methodName:(Type1)param1 withParam2:(Type2)param2;

이 메서드의 selector는 methodName:withParam2:가 됩니다.

더보기

Objective-C 메서드 선언 방법

  • 매개변수가 있는 경우: 각 매개변수마다 반드시 레이블(콜론 포함)이 있어야 합니다.
    • 첫 번째 매개변수: 첫 번째 매개변수의 레이블은 메서드 이름과 결합되어 있습니다.
    • - (void)doSomething:(int)param; // "doSomething:"이 메서드 이름+첫 매개변수 레이블
    • 두 번째 이후 매개변수: 각각 독립적인 레이블을 가져야 합니다.
    • - (void)doSomethingWith:(int)param1 and:(int)param2;

Java, C++, Python 등의 언어와 달리, Objective-C에서는 다음과 같은 문법이 불가능합니다.

// 불가능한 문법 (레이블 없는 매개변수)
- (void)calculate(int a, int b);   // 이렇게 선언할 수 없음

반드시 다음과 같이 각 매개변수에 레이블이 있어야 합니다.

// 올바른 Objective-C 문법
- (void)calculateWithA:(int)a andB:(int)b;

 

물론 매개변수 자체가 없는 메서드는 선언할 수 있습니다.

- (NSString *)description;   // 매개변수 없음
- (void)startProcess;        // 매개변수 없음

 

이 구문적 제약은 Objective-C의 메시지 전달 패러다임과 Smalltalk의 영향에서 비롯됩니다. 모든 메서드 호출이 "메시지 전송"으로 간주되며, 각 매개변수는 메시지의 일부로 명확히 레이블되어야 한다는 철학을 따릅니다.

이 구조는 코드의 가독성을 높이고 각 매개변수의 목적을 명확히 하는 데 도움이 되지만, 다른 언어에 익숙한 개발자들에게는 처음에 생소하고 장황하게 느껴질 수 있습니다.

Selector 생성 및 사용

// Selector 생성 방법
SEL selector1 = @selector(methodName);
SEL selector2 = @selector(methodName:withParam:);
SEL selector3 = NSSelectorFromString(@"methodName:withParam:");

// Selector 사용 예
if ([object respondsToSelector:@selector(methodName:)]) {
    [object performSelector:@selector(methodName:) withObject:parameter];
}

// Selector를 문자열로 변환
NSString *selectorName = NSStringFromSelector(@selector(methodName:));

Selector의 중요성

  • Target-Action 패턴: iOS/macOS 이벤트 처리의 기반이 됩니다.
[button addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
  • 프로토콜 적합성 검사: 객체가 특정 메서드를 구현했는지 확인할 수 있습니다.
if ([delegate respondsToSelector:@selector(didFinishTask:withResult:)]) { 
[delegate didFinishTask:task withResult:result]; 
}

 

 

  • 동적 메서드 호출: 런타임에 호출할 메서드를 결정할 수 있습니다.
SEL dynamicSelector = shouldUseAlternative ? 
	@selector(alternativeMethod:) : @selector(standardMethod:); 
[object performSelector:dynamicSelector withObject:parameter];
  • 메서드 존재 여부 확인: 객체가 특정 메서드를 구현했는지 확인할 수 있습니다.
if ([object respondsToSelector:@selector(optionalMethod:)]) { 
// 안전하게 메서드 호출 [object optionalMethod:parameter]; 
}
  • 지연된 메서드 호출: 나중에 실행할 메서드를 저장할 수 있습니다.
SEL callbackSelector = @selector(processResult:); 
[self performSelector:callbackSelector withObject:result afterDelay:1.0];
  • 런타임 메서드 스왑: 두 메서드의 구현을 교체할 수 있습니다.
Method original = class_getInstanceMethod([MyClass class], @selector(originalMethod)); 
Method swizzled = class_getInstanceMethod([MyClass class], @selector(swizzledMethod)); 
method_exchangeImplementations(original, swizzled);

Dispatch Table(디스패치 테이블)의 개념과 역할

Dispatch Table은 클래스에서 Selector를 해당 구현(implementation)에 매핑하는 런타임 데이터 구조입니다. 이 메커니즘은 Objective-C 메시지 전달 시스템의 핵심입니다.

Dispatch Table의 구조와 작동 방식

  1. 클래스별 테이블: 각 Objective-C 클래스는 자체 디스패치 테이블을 가집니다.
  2. 키-값 저장소: 내부적으로는 셀렉터(SEL)를 키로, 구현(IMP)을 값으로 하는 해시 테이블입니다.
    • SEL: 메서드 이름(셀렉터)
    • IMP: 실제 함수 포인터(메서드 구현)
  3. 계층적 검색: 메시지가 전송되면 다음 과정이 발생합니다
    • 객체의 클래스 디스패치 테이블에서 셀렉터 검색
    • 발견되지 않으면 부모 클래스의 디스패치 테이블로 이동
    • 체인을 따라 NSObject까지 올라가며 검색
  4. 캐싱 메커니즘: 성능 최적화를 위해 자주 사용되는 메서드 룩업 결과가 캐싱됩니다.