iOS 개발 에서 hook 메시지 메커니즘 을 실현 하 는 방법 탐구
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 을 추가 했다.경험 증 소식 의 유통 이 완전히 정상적이다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
View의 레이아웃 방법을 AutoLayout에서 따뜻한 손 계산으로 하면 성능이 9.26배로 된 이야기이 기사는 의 15 일째 기사입니다. 어제는 에서 이었습니다. 손 계산을 권하는 의도는 없고, 특수한 상황하에서 계측한 내용입니다 화면 높이의 10 배 정도의 contentView가있는 UIScrollView 레이아...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.