[Effective Objective-C] #13 불투명 메서드를 디버깅할 때 메서드 스위즐링을 사용하라
출처 : Effective Objective-C
-
Opaque Method ( 불투명 메소드 ) : 소스 코드를 볼 수 없는 메소드
-
동적 바인딩은 호출될 메서드가 실행 시간에 바뀔 수 있다는 것을 뜻한다.
이 능력은 굉장한 유연성을 제공하는데, 소스 코드가 없는 클래스 ( 즉 바이너리 파일만 있는 경우 ) 라도 하위 클래스를 만들거나 메서드를 재정의하지 않고도 기능을 변경할 수 있다.
그래서 하위 클래스에서 메서드를 재정의하는 방법은 하위 클래스의 인스턴스만 변경한 기능을 사용할 수 있는 것에 반해, 이 방법은 해당 클래스(기본 클래스와 모든 하위 클래스)의 모든 인스턴스에서 새로운 기능을 사용할 수 있다.
이 방법을 메서드 스위즐링(method swizzling)이라고 한다.
-
클래스의 메서드 목록은 키가 선택자의 이름이고 값이 메서드의 구현인 맵 형태다.
이 메서드 목록은 동적 메시징 시스템에서 메서드를 찾기 위해 사용한다.
메서드 구현은 IMP 라는 함수 포인터로 저장된다.
그 함수 포인터의 프로토타입은 다음과 같다.
id (*IMP)(id, SEL, …)
-
오브젝티브-C 런타임에 있는 몇 가지 함수를 이용해 이 선택자 테이블을 조작할 수 있다.
이 테이블에 새로운 선택자를 추가하거나 선택자가 가리키는 구현을 다른 것으로 바꿀 수 있다.
또 두 선택자 간에 구현을 서로 바꿀 수도 있다.
-
변경된 메서드 테이블은 어플레케이션에서 사용되는 모든 바꾼 대상 class의 인스턴스에 사용될 것이다.
-
구현을 서로 바꾸려면 다음 함수를 사용하면 된다.
void method_exchangeImplementations(Method m1, Method m2)
메서드는 다음 함수로 얻을 수 있다.
Method class_getInstanceMethod(Class aClass, SEL aSelector)
이 함수는 클래스에서 인자로 준 선택자에 해당하는 메서드를 찾아 반환한다.
-
NSString 의 lowercaseString 과 uppercaseString 의 맞교환은 다음과 같이 할 수 있다.
Method originalMethod = class_getInstanceMethod([NSString class], @selector(lowercaseString));
Method swappedMethod = class_getInstanceMethod([NSString class], @selector(uppercaseString));
method_exchangeimplementations(originalMethod, swappedMethod);
그러나 이미 구현되어 있는 두 구현을 맞교환하는 것이 실제로 쓸모 있는 경우는 별로 없다.
-
이미 존재하는 메서드 구현에 새로운 기능을 추가하기 위해 이와 같은 방법을 사용할 수 있다.
lowercaseString 이 호출될 때마다 특정 로그를 남기고 싶다면..
새로운 기능을 구현하는 또 다른 메서드를 추가하고 원래 메서드 구현과 맞바꿈으로써 할 수 있다.
추가 메서드는 카테고리를 사용한다.
@interface NSString (EOCMyAdditions)
- (NSString*)eoc_myLowercaseString;
@end
@implementation NSString (EOCMyAdditions)
- (NSString*)eoc_myLowercaseString{
NSString *lowercase = [self eoc_myLowercaseString];
NSLog(@“%@ => %@“, self, lowercase );
return lowercase;
}
@end
위의 메서드는 재귀 호출처럼 보이지만 구현이 바뀐다는 것을 기억해야 한다.
그렇기 때문에 실행 시간에 eoc_myLowercaseString 선택자를 찾으면 lowercaseString 의 구현이 찾아져 호출된다.
-
매우 유용한 디버깅 기능일 수 있지만 오직 디버깅 할 때에만 유용하다.
이와 같이 전역으로 클래스의 기능을 변경하기 위해 메서드 스위즐링를 하는 것은 디버깅 외에는 다른 사용 예를 찾기 어렵다.
이런 기능을 사용할 줄 안다고 꼭 사용할 필요는 없다.
과사용(overuse)은 코드를 읽기 어렵고 유지 보수가 어렵게 만드는 지름길이다.
기억할 점
클래스의 선택자에 대한 메서드 구현은 실행 시간에 추가되거나 바뀔 수 있다.
스위즐링은 한 메서드 구현을 다른 것과 맞교환하는 것이다.
보통은 원래 메서드 구현에 기능을 추가로 넣기 위해 사용된다.
런타임이 메서드에 관여하는 것(즉 실행할 메서드 구현을 결정하는 행위)은 오직 디버깅할 때만 유용하다.
사용할 수 있다고 꼭 사용해야 할 필요는 없다.
'프로그래밍 놀이터 > iOS' 카테고리의 다른 글
[Effective Objective-C] #15 접두어를 사용해 네임스페이스 충돌을 피하라 (0) | 2017.08.17 |
---|---|
[Effective Objective-C] #14 클래스 객체가 무엇인지 이해하라 (0) | 2017.08.16 |
[Effective Objective-C] #12 메시지 포워딩을 이해하라 (0) | 2017.08.14 |
[Effective Objective-C] #11 objc_msgSend 의 역할을 이해하라 (0) | 2017.08.13 |
[Effective Objective-C] #10 연관 객체를 사용해 기존 클래스에 사용자 정의 데이터를 연관 지으라 (0) | 2017.08.12 |
댓글