본문 바로가기
프로그래밍 놀이터/iOS

[Effective Objective-C] #13 불투명 메서드를 디버깅할 때 메서드 스위즐링을 사용하라

by 돼지왕 왕돼지 2017. 8. 15.
반응형

 [Effective Objective-C] #13 불투명 메서드를 디버깅할 때 메서드 스위즐링을 사용하라


출처 : Effective Objective-C

CLASS, class_getInstanceMethod, imp, Method, method swizzling, method_exchangeImplementations, opaque method, overuse, selector, [Effective Objective-C] #13 불투명 메서드를 디버깅할 때 메서드 스위즐링을 사용하라, 과사용, 디버깅, 맵, 메서드 구현, 메서드 목록, 메서드 스위즐링, 바이너리 파일, 불투명 메서드, 선택자, 선택자 이름, 선택자 테이블 조작, 소스 코드를 볼 수 없는 메소드, 스위즐링, 인스턴스, 재귀 호출, 재정의, 키, 프로토타입, 함수 포인터


-

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)은 코드를 읽기 어렵고 유지 보수가 어렵게 만드는 지름길이다.




기억할 점


클래스의 선택자에 대한 메서드 구현은 실행 시간에 추가되거나 바뀔 수 있다.


스위즐링은 한 메서드 구현을 다른 것과 맞교환하는 것이다.

보통은 원래 메서드 구현에 기능을 추가로 넣기 위해 사용된다.


런타임이 메서드에 관여하는 것(즉 실행할 메서드 구현을 결정하는 행위)은 오직 디버깅할 때만 유용하다.

사용할 수 있다고 꼭 사용해야 할 필요는 없다.




반응형

댓글