• ios开发runtime学习二:runtime交换方法


    #import "ViewController.h"
    
    /*
        Runtime(交换方法):主要想修改系统的方法实现
        
        需求:
     
        比如说有一个项目,已经开发了2年,忽然项目负责人添加一个功能,每次UIImage加载图片,告诉我是否加载成功
     
        当系统提供的控件不能满足我们的需求的时候,我们可以
     
        1:通过继承系统控件,重写系统的方法,来扩充子类的行为(super的调用三种情况)
        2:当需要为系统类扩充别的属性或是方法的时候,与哪个类有关系,就为哪个类创建分类。3:利用runtime修改系统的类,增加属性,交换方法,消息机制,动态增加方法
     
        解决方法:1:重写系统的方法:新建类继承系统的类,重写系统的方法(要是覆盖父类的行为就不需要调用super,或是在super方法之下调用:在保留父类super原有的行为后,扩充子类自己的行为,代码写在super之上,可以修改super要传递的参数,例如重写setframe,要是想保留父类的行为就不要忘记调用super)。弊端:需要在每个类中都需要引入头文件 2:写分类:为哪个系统的类扩充属性和方法,就为哪个类写分类 3:利用runtime底层的实现来修改或是访问系统的类:增加属性,交换方法,消息机制,动态增加方法
     
        3:本需求利用runtime:不需要导入头文件,调用的还是系统类原来的方法,只是利用了runtime的交换方法。
         给系统的imageNamed添加功能,只能使用runtime(交互方法)
         1.给系统的方法添加分类
         2.自己实现一个带有扩展功能的方法
         3.交互方法,只需要交互一次,
     
         1.自定义UIImage
         2.UIImage添加分类
     
     */
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // imageNamed => xmg_imageNamed 交互这两个方法实现
        UIImage *image = [UIImage imageNamed:@"1.png"];
        
    }
    
    
    @end
    #import "UIImage+Image.h"
    #import <objc/message.h>
    
    
    
    /**
     *  总结:
        1:  + (void)load与+ (void)initialize的区别:+ (void)load:当类加载进内存的时候调用,而且不管有没有子类,都只会调用一次,在main函数之前调用,用途:1:可以新建类在该类中实现一些配置信息 2:runtime交换方法的时候,因为只需要交换一次方法,所有可以在该方法中实现交换方法的代码,用于只实现一次的代码  2:+ (void)initialize:当类被初始化的时候调用,可能会被调用多次,若是没有子类,则只会调用一次,若是有子类的话,该方法会被调用多次,若是子类的继承关系,先会调用父类的+ (void)initialize方法,然后再去调用子类的+ (void)initialize方法(若是继承关系,调用某个方法的时候,先会去父类中查找,若是父类中没有方法的实现就去子类中查找) 用途:1:在设置导航栏的全局背景的时候,只需要设置一次,可以重写该方法设置,最好是在该方法判断子类,若是自己,则实现设置全局导航栏的方法,若不是自己则跳过实现。2:在创建数据库代码的时候,可以在该方法中去创建,保证只初始化一次数据库实例,也可以用dispatch或是懒加载的方法中初始化数据库实例,也能保证只初始化一次数据库实例。其中也可以在+ (void)initialize方法中用dispatch也能保证即使有子类也只会初始化一次
        
       2:交换方法:1:获取某个类的方法:class_getClassMethod:第一个参数:获取哪个类的方法 第二个参数:SEL:获取哪个方法
      
      Method imageNamedMethod = class_getClassMethod(self, @selector(imageNamed:));
     
     // 交互方法:runtime
      method_exchangeImplementations(imageNamedMethod, xmg_imageNamedMethod);
     
     也就是外部调用xmg_imageNamed就相当于调用了imageNamed,调用imageNamed就相当于调用了xmg_imageNamed
     
     3:在分类中,最好不要重写系统方法,一旦重写,把系统方法实现给干掉,因为分类不是继承父类,而是继承NSObject,super没有改类的方法,所以就直接覆盖掉了父类的行为
     
     */
    @implementation UIImage (Image)
    // 把类加载进内存的时候调用,只会调用一次
    + (void)load
    {
        // self -> UIImage
        // 获取imageNamed
        // 获取哪个类的方法
        // SEL:获取哪个方法
        Method imageNamedMethod = class_getClassMethod(self, @selector(imageNamed:));
        // 获取xmg_imageNamed
        Method xmg_imageNamedMethod = class_getClassMethod(self, @selector(xmg_imageNamed:));
        
        // 交互方法:runtime
        method_exchangeImplementations(imageNamedMethod, xmg_imageNamedMethod);
        // 调用imageNamed => xmg_imageNamedMethod
        // 调用xmg_imageNamedMethod => imageNamed
    }
    
    // 会调用多次
    //+ (void)initialize
    //{
    //    static dispatch_once_t onceToken;
    //    dispatch_once(&onceToken, ^{
    //        
    //    });
    //    
    //}
    
    // 在分类中,最好不要重写系统方法,一旦重写,把系统方法实现给干掉
    
    //+ (UIImage *)imageNamed:(NSString *)name
    //{
    //    // super -> 父类NSObject
    //
    //}
    
    // 1.加载图片
    // 2.判断是否加载成功
    + (UIImage *)xmg_imageNamed:(NSString *)name
    {
        // 图片
       UIImage *image = [UIImage xmg_imageNamed:name];
        
        if (image) {
            NSLog(@"加载成功");
        } else {
            NSLog(@"加载失败");
        }
        
        return image;
    }
    
    @end

    runtime 的实现原理:

    二:通过继承重写实现:每次都需要导入头文件,而且项目中很多地方都得需要修改

    #import <UIKit/UIKit.h>
    
    @interface XMGImage : UIImage
    
    @end
    #import "XMGImage.h"
    
    @implementation XMGImage
    
    // 重写方法:想给系统的方法添加额外功能
    + (UIImage *)imageNamed:(NSString *)name
    {
        // 真正加载图片:调用super初始化一张图片
        UIImage *image = [super imageNamed:name];
        
        if (image) {
            NSLog(@"加载成功");
        } else {
            NSLog(@"加载失败");
        }
        
        return image;
        
    }
    
    @end

    * 开发使用场景:系统自带的方法功能不够,给系统自带的方法扩展一些功能,并且保持原有的功能。

    * 方式一:继承系统的类,重写方法.

    * 方式二:使用runtime,交换方法.

    ```

    @implementation ViewController

    - (void)viewDidLoad {

        [super viewDidLoad];

        // Do any additional setup after loading the view, typically from a nib.

        // 需求:给imageNamed方法提供功能,每次加载图片就判断下图片是否加载成功。

        // 步骤一:先搞个分类,定义一个能加载图片并且能打印的方法+ (instancetype)imageWithName:(NSString *)name;

        // 步骤二:交换imageNamed和imageWithName的实现,就能调用imageWithName,间接调用imageWithName的实现。

        UIImage *image = [UIImage imageNamed:@"123"];

        

    }

    @end

    @implementation UIImage (Image)

    // 加载分类到内存的时候调用

    + (void)load

    {

        // 交换方法

        

        // 获取imageWithName方法地址

        Method imageWithName = class_getClassMethod(self, @selector(imageWithName:));

        

        // 获取imageWithName方法地址

        Method imageName = class_getClassMethod(self, @selector(imageNamed:));

        // 交换方法地址,相当于交换实现方式

        method_exchangeImplementations(imageWithName, imageName);

        

        

    }

    // 不能在分类中重写系统方法imageNamed,因为会把系统的功能给覆盖掉,而且分类中不能调用super.

    // 既能加载图片又能打印

    + (instancetype)imageWithName:(NSString *)name

    {

       

        // 这里调用imageWithName,相当于调用imageName

        UIImage *image = [self imageWithName:name];

        

        if (image == nil) {

            NSLog(@"加载空的图片");

        }

        

        return image;

    }

    @end

    ```

  • 相关阅读:
    let,const及解构赋值
    开发规范
    深入理解javascript之this
    深入理解javascript之作用域
    深入理解javascript之数据存储
    深入理解javascript之定时器
    Android 多选列表对话框 setMultiChoiceItems
    Android 单选列表对话框 setSingleChoiceItems
    Android 列表对话框 setItems
    Android 警告对话框 AlertDialog
  • 原文地址:https://www.cnblogs.com/cqb-learner/p/5871118.html
Copyright © 2020-2023  润新知