美文网首页
Objective-C Method Swizling 的使

Objective-C Method Swizling 的使

作者: Raybon_lee | 来源:发表于2015-12-12 17:56 被阅读529次

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

    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

    相关文章

      网友评论

          本文标题:Objective-C Method Swizling 的使

          本文链接:https://www.haomeiwen.com/subject/vxnyhttx.html