2016-08-17 5 views
1

나는 swisszling 메서드를 사용하고 있는데 method_exchangeImplementations을 수행 한 후에 원래 함수를 호출하고 싶습니다. 나는 이것을 위해 두 가지 프로젝트를 준비했다.swizzled 함수에서 원래 함수 호출하기

첫 번째 프로젝트는 응용 프로그램의 기본 프로젝트입니다. 이 프로젝트에는 응용 프로그램의 모든 논리가 포함됩니다. 뷰가로드 될 때 originalMethodName이 호출됩니다.

두 번째 프로젝트에는 스위 즐링 코드 만 포함됩니다. 난 originalMethodName 함수와 함께 주 응용 프로그램에 주입하려는 코드가 포함 된 swizzle_originalMethodName 메서드가 있습니다.

@implementation swizzle_ViewController 

- (void)swizzle_originalMethodName 
{ 
    NSLog(@"FAKE %s", __func__); 
} 

__attribute__((constructor)) static void initializer(void) 
{ 
    NSLog(@"FAKE %s", __func__); 

    Class c1 = objc_getClass("ViewController"); 
    Class c2 = [swizzle_ViewController class]; 
    Method m1 = class_getInstanceMethod(c1, @selector(originalMethodName)); 
    Method m2 = class_getInstanceMethod(c2, @selector(swizzle_originalMethodName)); 
    method_exchangeImplementations(m1, m2); 
} 

@end 

스위 즐 (아래 출력에서 ​​볼 수 있듯이) 잘 작동하지만, 지금은 내가 NSInvocation를 사용하려고 한 swizzle_originalMethodName

2016-08-17 14:18:51.765 testMacOS[7295:1297055] FAKE initializer 
2016-08-17 14:18:51.822 testMacOS[7295:1297055] REAL -[ViewController viewDidLoad] 
2016-08-17 14:18:51.822 testMacOS[7295:1297055] FAKE -[swizzle_ViewController swizzle_originalMethodName] 

에서 originalMethodName를 호출 할 수 싶지만입니다 운이 없다. 내가 뭘 잘못하고 있는거야?

Class c1 = objc_getClass("ViewController"); 
Method m1 = class_getInstanceMethod(c1, @selector(originalMethodName)); 
NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:method_getTypeEncoding( m1)]; 
NSInvocation *originalInvocation = [NSInvocation invocationWithMethodSignature:methodSignature]; 
[originalInvocation invoke]; 
+0

'[self swizzle_originalMethodName];을 (를) 호출하는 것은 어떻습니까? 그 선택자는 교환 후에 원래의 구현으로 되돌아 가야한다. –

+0

이것은 천재입니다. 왜 이것이 나에게 일어나지 않았는지 모르겠습니다. 그래도 여전히 오류가 발생하고 해결 방법을 모르겠습니다. '- [ViewController swizzle_originalMethodName] : 인식 할 수없는 셀렉터가 인스턴스로 전송되었습니다. 0x7ff72974a670' –

+0

아, 맞아 ... 당신은 다른 클래스를 뒤 흔드는 군. 나는 그것에 대해 생각할 필요가있을 것이다. –

답변

1

예를 들어, 클래스 계층 내에서 swizzling하는 경우 당신은 조상 메소드 중 하나를 자신의 것으로 조화시키는 서브 클래스를 가지고있다. 그러면 swchiezled-in 메소드 이 분명히있다.은 그 자체를 호출한다 - 그 메소드는 메소드가 스왑 된 것처럼 실제로 swizzled-out 메소드를 호출 할 것이다. 귀하의 경우에는, 당신은 것입니다 : 당신이 교차 클래스 스위 즐링이기 때문에 이것은, 귀하의 경우에는 작동하지 않습니다 그래서 self는 스위 즐링 아웃 방법으로 클래스를 참조하지 않습니다

- (void)swizzle_originalMethodName 
{ 
    NSLog(@"FAKE %s", __func__); 
    [self swizzle_originalMethodName]; // call original 
} 

. 그리고 당신은 swizzle-out 메서드를 호출 할 수있는 swizzle 클래스의 인스턴스를 가지고 있지 않습니다. ...

여기를 수정하는 쉬운 방법이 있습니다. swizzle-in 메서드로 할 수있는 일은 다음과 같습니다. 원래 구현을 호출하고, 당신이 swizzling을 설정할 때 얻을 수 있습니다.

Objective-C에서 메서드는 처음 두 개의 인수가 메서드가 호출되는 개체 참조이며 선택자와 나머지 인수는 메서드의 인수입니다. 예를 들어, NSString 방법 :

- (NSRange)rangeOfString:(NSString *)aString 

이 같은 기능 무언가에 의해 구현됩니다

NSRange rangeOfStringImp(NSString *self, SEL cmd, NSString *aString) 

당신은 method_getImplementation을 사용하여이 구현 함수에 대한 함수 포인터를 얻을 수 있습니다.swizzle_ViewController 당신이 스위 즐링하는 방법의 구현 함수에 대한 유형을 선언하고 글로벌 함수 포인터를 저장하기 위해 당신의 코드에

, 먼저 : 당신의 initializer 방법 지금

typedef void (*OriginalImpType)(id self, SEL selector); 
static OriginalImpType originalImp; 

을 당신이 필요 메소드 구현을 저장, 당신은 표시된 줄을 추가하여이 작업을 수행 할 수 있습니다

Method m1 = class_getInstanceMethod(c1, @selector(originalMethodName)); 
originalImp = (OriginalImpType)method_getImplementation(m1); // save the IMP of originalMethodName 

마지막으로 당신의 스위 즐링 된 방법은 저장 구현 전화가 :

- (void)swizzle_originalMethodName 
{ 
    NSLog(@"FAKE %s", __func__); 
    originalImp(self, @selector(originalMethodName)); // call the original IMP with the correct self & selector 
} 

선택 사항 : 위의 코드는 제대로 작동하지만 필요한 것 이상을 수행합니다. 메소드 구현은 모두 교환되고 전역 변수에 저장됩니다. 실제로 수행해야 할 작업은 m1의 원래 구현을 저장하는 것입니다. 구현을 m2으로 설정하십시오. 당신은 함께 method_exchangeImplementations에 전화를 대체하여이 문제를 해결 할 수 있습니다

method_setImplementation(m1, method_getImplementation(m2)); 

그것은 좀 더 타이핑, 실제로 일을해야 무엇인지에 다소 명확하다.

HTH

+0

당신은 최고입니다! 이 작품! 어제는이 일을하려고 몇 시간 동안 벽에 머리를 때렸다. 고맙습니다 –