iOS 개발 에서 hook 메시지 메커니즘 을 실현 하 는 방법 탐구

7714 단어 iOS
방법 Swizzling 원리
Objective-C 에서 하나의 방법 을 호출 합 니 다.사실은 한 대상 에 게 메 시 지 를 보 내 는 것 입 니 다.메 시 지 를 찾 는 유일한 근 거 는 selector 의 이름 입 니 다.Objective-C 의 동적 특성 을 이용 하여 실행 할 때 selector 를 훔 쳐 대응 하 는 방법 을 실현 하여 방법 에 연결 하 는 목적 을 달성 할 수 있 습 니 다.
클래스 마다 하나의 방법 목록 이 있 습 니 다.selector 의 이름과 방법 으로 이 루어 진 맵 관 계 를 저장 합 니 다.IMP 는 함수 포인터 와 유사 하여 구체 적 인 Method 를 가리 키 고 있 습 니 다.

우 리 는 method 를 이용 할 수 있다.exchange Implementations 는 두 가지 방법 중 IMP 를 교환 합 니 다.
우 리 는 class 를 이용 할 수 있다.replace Method 에서 클래스 를 수정 합 니 다.
우 리 는 method 를 이용 할 수 있다.setImplementation 은 어떤 방법의 IMP 를 직접 설정 합 니 다.
……
결국 selector 의 IMP 를 훔 쳤 습 니 다.다음 그림 과 같 습 니 다.

방법 Swizzling 실천
예 를 들 어 NSArray 의 lastObject 방법 을 연결 하려 면 두 단계 만 있 으 면 됩 니 다.
STEP 1:NSArray 에 내 lastObject 추가

#import "NSArray+Swizzle.h" 
 
 
@implementation NSArray (Swizzle) 
 
 
- (id)myLastObject 

    id ret = [self myLastObject]; 
    NSLog(@"**********  myLastObject *********** "); 
    return ret; 

@end 
언뜻 보기에 이것 은 귀속 되 지 않 습 니까?이것 은 우리 가 IMP 를 바 꾸 려 고 하 는 selector 라 는 것 을 잊 지 마 세 요.[self my LastObject]는 진짜[self lastObject]를 실행 할 것 입 니 다.
STEP 2:IMP 교체

#import  
#import "NSArray+Swizzle.h" 
 
 
int main(int argc, char *argv[]) 

    @autoreleasepool { 
         
        Method ori_Method =  class_getInstanceMethod([NSArray class], @selector(lastObject)); 
        Method my_Method = class_getInstanceMethod([NSArray class], @selector(myLastObject)); 
        method_exchangeImplementations(ori_Method, my_Method); 
         
        NSArray *array = @[@"0",@"1",@"2",@"3"]; 
        NSString *string = [array lastObject]; 
        NSLog(@"TEST RESULT : %@",string);  
          
        return 0; 
    } 
콘 솔 출력 로그:

2013-07-18 16:26:12.585 Hook[1740:c07] **********  myLastObject ***********  
2013-07-18 16:26:12.589 Hook[1740:c07] TEST RESULT : 3 
결 과 는 반 가 웠 습 니 다.UIWebView 의 loadRequest:TODO 를 주 고 싶 은 마음 을 참 을 수 없 었 나 요?
예시
이 원리 가 있 으 면 다음 에 우 리 는 실례 를 보 자.
다음 에 먼저 소스 코드 를 올 립 니 다.
 

//
//  TestHookObject.m
//  TestHookMessage
//
//  Created by mapleCao on 13-2-28.
//  Copyright (c) 2013 mapleCao. All rights reserved.
//

#import "TestHookObject.h"
#import <objc/objc.h>
#import <objc/runtime.h>

@implementation TestHookObject

// this method will just excute once
+ (void)initialize
{
    // UIWindow sendEvent method
    Method sendEvent = class_getInstanceMethod([UIWindow class], @selector(sendEvent:));
    Method sendEventMySelf = class_getInstanceMethod([self class], @selector(sendEventHooked:));
   
    // sendEventOriginalImplemention
    IMP sendEventImp = method_getImplementation(sendEvent);
    class_addMethod([UIWindow class], @selector(sendEventOriginal:), sendEventImp, method_getTypeEncoding(sendEvent));
   
    // ,
    IMP sendEventMySelfImp = method_getImplementation(sendEventMySelf);
    class_replaceMethod([UIWindow class], @selector(sendEvent:), sendEventMySelfImp, method_getTypeEncoding(sendEvent));
}

/*
 * window sendEvent
 * ,
 */
- (void)sendEventHooked:(UIEvent *)event
{
    // do something what ever you want
    NSLog(@"haha, this is my self sendEventMethod!!!!!!!");
   
    // invoke original implemention
    [self performSelector:@selector(sendEventOriginal:) withObject:event];
}

@end

다음은 위의 코드 를 한 줄 한 줄 분석 해 보 겠 습 니 다.
 
먼저 19 줄 을 살 펴 보 겠 습 니 다.이 줄 의 주요 목적 은 UIWindow 원생 sendEvent 의 Method(하나의 구조 체,방법 에 대한 설명)를 얻 는 것 입 니 다.이 어 20 줄 은 우리 가 정의 하 는 클래스 의 sendEvent 의 Method 를 얻 는 것 입 니 다.(이 두 가지 방법의 서명 은 반드시 같 아야 합 니 다.그렇지 않 으 면 타 임 스 가 잘못 되 었 습 니 다)23 번 째 줄 은 UIWindow 네 이 티 브 sendEvent 의 Method 를 통 해 해당 하 는 IMP(함수 포인터 하나)를 가 져 옵 니 다.24 번 째 줄 은 런 타임 API Class 를 사용 합 니 다.addMethod 는 UIWindow 클래스 에 sendEventOriginal 이라는 방법 을 추가 하 였 습 니 다.이 방법 은 UIWindow 원생 의 sendEvent 를 사용 하여 이 루어 지고 같은 방법 으로 서명 해 야 합 니 다(같 아야 합 니 다.그렇지 않 으 면 타 임 스 가 잘못 되 었 습 니 다).27 줄 은 사용자 정의 클래스 의 sendEventMySelf 를 가 져 오 는 IMP 입 니 다.28 줄 은 관건 적 인 줄 입 니 다.이 줄 의 주요 목적 은 UIWindow 원생 의 sendEvent 에 새로운 실현 을 지정 하 는 것 입 니 다.우 리 는 이 실현 을 우리 가 정의 한 sendEventMySelf 에 지정 하 는 것 을 보 았 습 니 다.여기까지 와 서 우 리 는 대 들 보 를 훔 쳐 기둥 을 바 꾸 어 큰 성 과 를 거 두 었 다.
 
위의 줄 을 실행 한 후에 우 리 는 UIWindow 의 sendEvent 를 우리 가 쓴 sendEventMySelf 의 실현 으로 성공 적 으로 재 설정 한 다음 에 그 원래 의 실현 을 우리 가 새로 추가 하 는 방법 인 sendEventOriginal 로 재 설정 했다.한편,sendEventMySelf 에서 우 리 는 먼저 이 소식 에 대해 우리 가 원 하 는 처 리 를 한 다음 에 41 줄 을 통 해 sendEventOriginal 방법 을 호출 하여 정상 적 인 실행 절차 로 전환 할 수 있 습 니 다.
 
여기 서 곤 혹 스 러 울 수도 있 습 니 다."우리 사용자 정의 클래스 에는 분명히 sendEvent Original 방법 이 없 는데?"
 
왜 집행 하기 시작 하면 잘못 을 보고 하지 않 고 정상적으로 집행 합 니까?sendEventMySelf 는 UIWindow 의 sendEvent 에서 방향 을 바 꾸 었 기 때문에 실행 할 때 이 방법 에서 self 는 UIWindow 의 인 스 턴 스 를 대표 합 니 다.TestHook Object 의 인 스 턴 스 가 아 닙 니 다.게다가 sendEventOriginal 은 우리 가 실행 할 때 UIWindow 에 추가 하 는 인 스 턴 스 방법 이기 때문에 정상적으로 호출 할 수 있 습 니 다.물론 아래 와 같은 방식 으로 직접 호출 하 는 것 도 가능 합 니 다.다만 컴 파일 러 는 경고(컴 파일 러 가 그렇게 스마트 하지 않 음)를 표시 하기 때문에 저 희 는 performSelector 의 호출 방식 을 사 용 했 습 니 다.
 

[self sendEventOriginal:event];
이상 은 Hook 의 실현 입 니 다.사용 할 때 TestHook Object 류 에 게 초기 화 작업 을 한 번 만 수행 하 게 하면 됩 니 다.실행 이 끝 난 후에.UIWindow 의 sendEvent 메 시 지 는 우리 의 sendEventMySelf 에 훅 됩 니 다.
 
다음은 호출 코드 입 니 다.
 
 
 

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    // Override point for customization after application launch.
    self.viewController = [[[TestHookViewController alloc] initWithNibName:@"TestHookViewController" bundle:nil] autorelease];
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];
   
   
    //hook UIWindow‘s SendEvent method
    TestHookObject *hookSendEvent = [[TestHookObject alloc] init];
    [hookSendEvent release];
   
    UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    btn.center = CGPointMake(160, 240);
    btn.backgroundColor = [UIColor redColor];
    [btn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventAllEvents];
    [self.window addSubview:btn];
    [btn release];
   
    return YES;
}

코드 에 우 리 는 훅 이 끝 난 후에 메시지 가 정상적으로 전달 되 는 지 확인 하기 위해 button 을 추가 했다.경험 증 소식 의 유통 이 완전히 정상적이다.

좋은 웹페이지 즐겨찾기