Objective-C Method Swizling 的使用学习

既然是一个程序猿,就要不断的学习,虽然没有地位,但是为了家庭,你说要不要生活,哎,算了,开始说我们的话题吧,

Method Swizling --动态交换IMP实现指针

在OC中调用一个方法,其实并不是方法,而是找到内存地址的一个字符串地址,查找是根据@selector 关键字而来的,利用Objective-C的动态特性,可以实现在运行时偷换selector 对应的方法实现,从而达到给方法挂钩的目的
每个类都有一个方法列表,存放着selector的方法和名字实现的映射关系,
IMP 也可以理解为OC中的implements 指针实现。

内存中的方法选择器和实现指针映射关系表

我们常用的动态交换方法有系统提供给我们几个方法

1. method_exchangeImplementations    交换两个方法
2. class_replaceMethod  替换方法
3. method_setImplementation  设置实现方法
 
动态交换内部实现方法

看到上面的图,我们是否可以想到两个指针交换数据,我们不能简单的交换指针名字,而是交换了指针地址

对于这一块的东西理解的也是毛坯,等有时间继续了解一下,目前我根据我看到的资料,分享一下简单的使用

我们来获取控制器的viewdidload 方法 viewWillAppear 出现的次数,我们进行动态的交换方法,

我们先写一个动态交换方法

第一步,我们新建一个Category ,名字为 UIViewController+AOP

我们来写一个静态实例交换方法

//originalSelector  原来方法
// swizzleSelector 动态运行时的交换方法
void swizzleMethod(Class class,SEL originalSelector,SEL swizzleSelector){
    //class_getInstanceMethod返回 class的名称为selector的方法
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzleSelector);
    //method_getImplementation  返回method的实现指针
    BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    if(didAddMethod){
        //class_replaceMethod  替换函数实现  函数  originalMethod 用swizzleSelector  替换
        class_replaceMethod(class, swizzleSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        
    }else{
        //交换两个IMP是实现指针
        method_exchangeImplementations(originalMethod, swizzledMethod);
        
    }
    
}

以上都有注释,这里就不多写明了,下面我们看下系统提供的交换方法的API,

// 交换两个方法,传入的参数是两个@selector 方法
OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2) 
    __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

//替换方法
参数含义
Class class : 需要替换方法的类名
SEL name :  需要替换的方法名字
IMP imp : 需要实现的方法名字
const char * types :原方法的名字

OBJC_EXPORT IMP class_replaceMethod(Class cls, SEL name, IMP imp, 
                                   const char *types) 
    __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

//设置实现指针
参数 :
Method m: 方法名字
IMP imp : 实现指针
OBJC_EXPORT IMP method_setImplementation(Method m, IMP imp) 
    __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

为什么我们要这么麻烦写这么一段代码,接手维护的项目大多都不好管理,需要进行重构,我们尽可能的会用少量的代码简化我的方法体

下面我们继续实现类中一定会调用的方法load 方法

+ (void)load{
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
       Class class = [self class];
       swizzleMethod(class, @selector(viewDidLoad), @selector(aop_viewDidLoad:));
       swizzleMethod(class, @selector(viewDidAppear:), @selector(aop_viewDidAppear:));
       swizzleMethod(class, @selector(viewWillAppear:), @selector(aop_viewWillAppear:));
       swizzleMethod(class, @selector(viewWillDisappear:), @selector(aop_viewWillDisAppear:));
       
       
   });
}

问题:+(void)load VS +(void)initialize
每个类的这两个方法都会被Objective-C运行时系统自动调用,+load 方法是在一个类最开始调用的,+initialize 是在应用中第一次调用该类或者他的实例的方式之前调用,这两个方法都可以使用,只有实现了才会被执行,

既然是分类,就会全局监听,+load 能够保证在类初始化的时候会被加载,通过这个操作可以做一些统一的操作,但是+initialize 并不能保证什么时候被调用,有可能永远也不会被调用,如果应用程序从未直接给该类发送任何消息,则无法调用

在调用load 方法的时候,我们通常是通过
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
//我们采取了一个预防措施,防止运行时多次被执行,确保代码即使使用在多线程环境下也只会被执行一次,我们优先使用GCD

});

调用_cmd
下面这段代码不会影响执行,也不会进入死循环,
aop_viewWillAppear 这个方法会在运行时进入到协同的viewWillAppear 方法执行,但是我们不能在这个里面调用viewWillAppear,以免造成无法控制的危险

- (void)aop_viewWillAppear:(BOOL)animated{
    [self aop_viewWillAppear:animated];
    NSLog(@"111111111111");
}

下面我们给出源代码整体的实现思路,

#import <UIKit/UIKit.h>

@interface UIViewController (AOP)

- (void)aop_viewWillAppear:(BOOL)animated;

- (void)aop_viewDidAppear:(BOOL)animated;

- (void)aop_viewDidLoad:(BOOL)animated;

- (void)aop_viewWillDisAppear:(BOOL)animated;
@end



#import "UIViewController+AOP.h"
#import <objc/runtime.h>
@implementation UIViewController (AOP)
+ (void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        swizzleMethod(class, @selector(viewDidLoad), @selector(aop_viewDidLoad:));
        swizzleMethod(class, @selector(viewDidAppear:), @selector(aop_viewDidAppear:));
        swizzleMethod(class, @selector(viewWillAppear:), @selector(aop_viewWillAppear:));
        swizzleMethod(class, @selector(viewWillDisappear:), @selector(aop_viewWillDisAppear:));
        
        
    });
}
void swizzleMethod(Class class,SEL originalSelector,SEL swizzleSelector){
    //class_getInstanceMethod返回 class的名称为selector的方法
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzleSelector);
    //method_getImplementation  返回method的实现指针
    BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    if(didAddMethod){
        //class_replaceMethod  替换函数实现  函数  originalMethod 用swizzleSelector  替换
        class_replaceMethod(class, swizzleSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        
    }else{
        //交换两个IMP是实现指针
        method_exchangeImplementations(originalMethod, swizzledMethod);
        
    }
    
}
- (void)aop_viewWillAppear:(BOOL)animated{
    [self aop_viewWillAppear:animated];
    NSLog(@"111111111111");
}
- (void)aop_viewDidAppear:(BOOL)animated{
    [self aop_viewDidAppear:animated];
     NSLog(@"22222222222");
}

- (void)aop_viewDidLoad:(BOOL)animated{
    [self aop_viewDidLoad:animated];
     NSLog(@"33333333333333");
    if([self isKindOfClass:[UINavigationController class] ]){
        UINavigationController * nav =(UINavigationController *)self;
        nav.navigationBar.translucent = NO;
        nav.navigationBar.tintColor = [UIColor whiteColor];
        nav.navigationBar.barTintColor = [UIColor greenColor];
        NSDictionary * titleAtt = @{NSForegroundColorAttributeName:[UIColor whiteColor]};
        [[UINavigationBar appearance]setTitleTextAttributes:titleAtt];
        [[UIBarButtonItem appearance ] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60) forBarMetrics:UIBarMetricsDefault];
        
    }
    self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;
    
}
- (void)aop_viewWillDisAppear:(BOOL)animated{
    [self aop_viewWillDisAppear:animated];
#ifndef DEBUG
     NSLog(@"44444444");
#endif
}
@end

以上是整体的代码
这种方法可以来控制我们的代码减少代码结构,
文章如有错误还望大神给予指正,在这里只是学习一下,
iOS 学习交流群:392633290 加群备注:(简书)
Apple 官方的 Objective-C Runtime Reference objc/runtime

同时附上原文链接:
OC动态运行时使用Method Swizling

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容