原理和上篇文章一樣 : https://blog.csdn.net/qq_15509071/article/details/102688400
這裏直接貼代碼
#import "NSBundle+yyy.h"
#import <objc/runtime.h>
@implementation NSBundle (yyy)
+ (void)load{
// NSString *bundleid = [[self mainBundle] bundleIdentifier];
{
Method originalMethod = class_getInstanceMethod([NSBundle class], @selector(bundleIdentifierS));
Method swizzledMethod = class_getInstanceMethod([NSBundle class], @selector(bundleIdentifier));
method_exchangeImplementations(originalMethod, swizzledMethod);
}
{
Method originalMethod = class_getInstanceMethod([NSBundle class], @selector(infoDictionaryS));
Method swizzledMethod = class_getInstanceMethod([NSBundle class], @selector(infoDictionary));
method_exchangeImplementations(originalMethod, swizzledMethod);
}
// {
// Method originalMethod = class_getInstanceMethod([NSBundle class], @selector(localizedInfoDictionaryS));
// Method swizzledMethod = class_getInstanceMethod([NSBundle class], @selector(localizedInfoDictionary));
// method_exchangeImplementations(originalMethod, swizzledMethod);
//
// }
}
- (NSDictionary*)infoDictionaryS{
NSMutableDictionary *dicc = [self infoDictionaryS].mutableCopy;
if ([Global sharedData].useHookBundleid == YES) {
[dicc setObject:@"需要的" forKey:@"CFBundleIdentifier"];
}
return dicc;
}
- (NSString *)bundleIdentifierS{
if ([Global sharedData].useHookBundleid == YES) {
return @"需要的";
}else{
return [self bundleIdentifierS];
}
}
識別身份證的調用方法
[EXOCRCardEngineManager initEngine];
self.manager = [EXOCRQuadRecoManager sharedManager:、];
[self.manager setDisplayLogo:NO];
[self.manager setRecoTimeout:90];
setCustomScanView
[self.manager recoQuadCardFromStreamByCustomScanViewWithCardType:EXOCRCardTypeIDCARD];
協議方法
/**
* @brief 識別完成,獲取識別結果
* @param info - 識別結果模型
*/
-(void)recoCompleted:(id)info;{
//識別成功後的操作
[self.manager dismissScanViewControllerAnimated:YES];
}
initEngine 裏面寫 useHookBundleid = YES;
由於協議方法是在實現的類裏實現的,所以useHookBundleid = No,不能按照之前的思路。
方法1:識別成功都調用了dismissScanViewControllerAnimated:YES,在這個方法里加上。但如果沒調用就會有問題
@implementation EXOCRQuadRecoManager (YYY)
+ (void)load{
// {
// Method originalMethod = class_getInstanceMethod([self class], @selector(dismissScanViewControllerAnimated:));
// Method swizzledMethod = class_getInstanceMethod([self class], @selector(dismissScanViewControllerAnimatedS:));
// method_exchangeImplementations(originalMethod, swizzledMethod);
//
// }
}
//////掃描身份證結束
//-(void)dismissScanViewControllerAnimatedS:(BOOL)animated;{
//
// [Global sharedData].useHookBundleid = NO;
//
// [self dismissScanViewControllerAnimatedS:animated];
//}
方法2:參考:https://www.jianshu.com/p/2d9774c8b224 對代理方法hook。哪個類實現了代理給哪個類替換方法。
@interface EXOCRQuadRecoManager (YYY)
@end
@implementation EXOCRQuadRecoManager (YYY)
+ (void)load{
{
//交換的是設置代理的方法
Method originalMethod = class_getInstanceMethod([self class], @selector(setCustomScanView:));
Method swizzledMethod = class_getInstanceMethod([self class], @selector(setCustomScanViewS:));
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
- (BOOL)setCustomScanViewS:(UIView *)customScanView{
BOOL isSc = [self setCustomScanViewS:customScanView];
dz_exchangeMethod([customScanView class], @selector(recoCompleted:), [self class], @selector(recoCompletedS:),@selector(orirecoCompleted:));
return isSc;
}
- (void)recoCompletedS:(UIWebView *)webView
{
[Global sharedData].useHookBundleid = NO;
[self recoCompletedS:webView];
}
static void dz_exchangeMethod(Class originalClass, SEL originalSel, Class replacedClass, SEL replacedSel, SEL orginReplaceSel){
// 原方法
Method originalMethod = class_getInstanceMethod(originalClass, originalSel);
// 替換方法
Method replacedMethod = class_getInstanceMethod(replacedClass, replacedSel);
// 如果沒有實現 delegate 方法,則手動動態添加
if (!originalMethod) {
Method orginReplaceMethod = class_getInstanceMethod(replacedClass, orginReplaceSel);
BOOL didAddOriginMethod = class_addMethod(originalClass, originalSel, method_getImplementation(orginReplaceMethod), method_getTypeEncoding(orginReplaceMethod));
if (didAddOriginMethod) {
NSLog(@"did Add Origin Replace Method");
}
return;
}
// 向實現 delegate 的類中添加新的方法
// 這裏是向 originalClass 的 replaceSel(@selector(replace_webViewDidFinishLoad:)) 添加 replaceMethod
BOOL didAddMethod = class_addMethod(originalClass, replacedSel, method_getImplementation(replacedMethod), method_getTypeEncoding(replacedMethod));
if (didAddMethod) {
// 添加成功
NSLog(@"class_addMethod_success --> (%@)", NSStringFromSelector(replacedSel));
// 重新拿到添加被添加的 method,這裏是關鍵(注意這裏 originalClass, 不 replacedClass), 因爲替換的方法已經添加到原類中了, 應該交換原類中的兩個方法
Method newMethod = class_getInstanceMethod(originalClass, replacedSel);
// 實現交換
method_exchangeImplementations(originalMethod, newMethod);
}else{
// 添加失敗,則說明已經 hook 過該類的 delegate 方法,防止多次交換。
NSLog(@"Already hook class --> (%@)",NSStringFromClass(originalClass));
}
}
@end
下面是另一種使用方法
需要用到的地方:身份證識別
[Global sharedData].useHookBundleid = YES;
[EXOCRCardEngineManager initEngine];
self.manager = [EXOCRQuadRecoManager sharedManager:self.controller];
[self.manager recoQuadCardParticularFromStreamWithCardType:EXOCRCardTypeIDCARD OnParticularCompleted:^(int statusCode, id cardInfo) {
[Global sharedData].useHookBundleid = NO;
//。。。
} OnCanceled:^(int statusCode) {
[Global sharedData].useHookBundleid = NO;
} OnFailed:^(int statusCode, UIImage *recoImg) {
[Global sharedData].useHookBundleid = NO;
}];
這樣也是每一個地方都要寫。簡化一下
在上邊的類中添加如下代碼
{
Method originalMethod = class_getInstanceMethod([self class], @selector(recoQuadCardParticularFromStreamWithCardType:OnParticularCompleted:OnCanceled:OnFailed:));
Method swizzledMethod = class_getInstanceMethod([self class], @selector(recoQuadCardParticularFromStreamWithCardType:OnParticularCompleted:OnCanceled:OnFailedS:));
method_exchangeImplementations(originalMethod, swizzledMethod);
}
-(void)recoQuadCardParticularFromStreamWithCardType:(EXOCRCardType)cardType
OnParticularCompleted:(EXOCRCompletedQuadCardParticularBlock)completedBlock
OnCanceled:(EXOCRCanceledBlock)EXOCRCanceledBlock
OnFailedS:(EXOCRFailedBlock)EXOCRFailedBlock;{
[self recoQuadCardParticularFromStreamWithCardType:cardType OnParticularCompleted:^(int statusCode, id cardInfo) {
[Global sharedData].useHookBundleid = NO;
completedBlock(statusCode,cardInfo);
} OnCanceled:^(int statusCode) {
[Global sharedData].useHookBundleid = NO;
EXOCRCanceledBlock(statusCode);
} OnFailedS:^(int statusCode, UIImage *recoImg) {
[Global sharedData].useHookBundleid = NO;
EXOCRFailedBlock(statusCode,recoImg);
}];
}