iOS常识runtime.runloopiOS 开发之 runtime

在iOS中用Runtime可以做什么?

2020-09-25  本文已影响0人  CyberDunk1997

iOS 常见知识点(一):Runtime:https://www.jianshu.com/p/965bd18cb056
https://www.jianshu.com/p/adf0d566c887
iOS开发-runtime-消息传递和转发机制:http://zhangzr.cn/2018/02/08/iOS%E5%BC%80%E5%8F%91-runtime-%E6%B6%88%E6%81%AF%E4%BC%A0%E9%80%92%E5%92%8C%E8%BD%AC%E5%8F%91%E6%9C%BA%E5%88%B6/

iOS运行时(Runtime)详解+Demo:https://www.jianshu.com/p/adf0d566c887

1.什么是Runtime

2.runtime怎么使用

1.先导入头文件

#import <objc/message.h>

2.再调用runtime的方法

//普通方法创建对象p
Person *p = [[Person alloc]init];

//runtime创建对象p
Person *p = objc_msgSend(objc_getClass("Person"),sel_registerName("alloc")); // Person = [p alloc];
p = objc_msgSend(p,sel_registerName("init")); // p = [p init];

//调用不带参数的方法
objc_msgSend(p, @selector(eat) ); //[p eat];

//调用带参数的方法
objc_msgSend(p, @selector(run) , 20); //[p run:20];

3.什么时候用runtime

4. 方法调用的流程(消息机制)

须知:
对象方法,保存在对应的类的方法列表中
类方法,保存在对应元类的(meta class)方法列表中
[p eat];为例

  1. 寻找p对应的类,即Person类。每个对象都有一个isa指针,指针指向它对应的类,再去类的方法列表中查找对应方法。在[p eat];中,pisa指针指向了Person类(p.isa -> Person)
  2. 注册一个方法编号,sel_registerName("eat")(操作数字比操作字符串快)
  3. 根据方法编号查找对应方法,(在[p eat];中,再去Person类中的方法列表(MethodList)中寻找对应方法)
  4. 根据方法地址去方法区调方法的实现
方法调用流程

5.利用runtime交换方法

需求:改造[UIImage imageNamed:@"1.png"]; 方法,可以检测是否成功加载图片。

方法一:自定义UIImage,重写[UIImage imageNamed:]方法。
#import "CYBImage.h"

@implementation CYBImage

+(UIImage *)imageNamed:(NSString *)name{
    UIImage *image = [super imageNamed:name];
    
    if (image){
        NSLog(@"加载成功");
    }else{
        NSLog(@"加载失败");
    }
     
    return image;
}

@end
#import "CYBImage.h"

CYBImage *image = [CYBImage imageNamed:@"1.png"];

//2020-09-25 10:49:30.397798+0800 [54068:2433122] 加载失败
方法二:给UIImage添加分类

对UIImage添加Category分类,super ——>指向父类(NSObject),父类NSObject中无法使用[ UIImage imageNamed:]这个方法

方法三:使用runtime修改系统的方法

想要修改系统的自带类,只能用runtime解决(交换方法)。

步骤:
  1. 给系统的方法添加分类
  2. 添加一个带有扩展功能的方法
#import "UIImage+image.h"

@implementation UIImage (image)

+(UIImage *)cyb_imageNamed:(NSString *)name{
    //1.加载图片
    UIImage *image = [UIImage imageNamed:name];
    //2.判断加载成功
    if(image) {
        NSLog(@"cyb_imageNamed加载成功");
    }else {
        NSLog(@"cyb_imageNamed加载失败");
    }
    
    return  image;
}
@end
  1. 交换两个方法的实现,只需要交换一次
//把类加载进内存的时候调用,只会调用一次,swift里面没有+load
+(void)load
{
    //获取imageNamed
    //参数1:获取哪个类
    //参数2:获取这个类的哪个方法
    Method imageNamed = class_getClassMethod(self, @selector(imageNamed:));
    //获取cyb_imageNamed
    Method cyb_imageNamed = class_getClassMethod(self, @selector(cyb_imageNamed:));
    //交换方法
    method_exchangeImplementations(imageNamed, cyb_imageNamed);
}
//会调用多次,swift可以用这种方法
+(void)initialize
{
    //dispatch_once可以保证只调用一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
         
    });
}
  1. 交换使用到imageNamed:cyb_imageNamed:的地方
+(UIImage *)cyb_imageNamed:(NSString *)name{

    -----------------------这里交换了使用的方法------------------------
    UIImage *image = [UIImage cyb_imageNamed:name];
    -----------------------这里交换了使用的方法------------------------

    if(image) {
        NSLog(@"cyb_imageNamed加载成功");
    }else {
        NSLog(@"cyb_imageNamed加载失败");
    }
    
    return  image;
}
  1. 测试是否交换成功
#import "UIImage+image.h"

UIImage *image = [UIImage imageNamed:@"1.png"];

//2020-09-25 11:16:56.735535+0800 [54357:2444466] cyb_imageNamed加载失败

图解方法交换

方法交换之前 方法交换之后

6. 用Runtime动态添加方法

[p performSelector: @selector(eat)];

#import <objc/message.h>
//当调用了一个没有实现的方法时,调用这个方法
+(BOOL)resolveInstanceMethod:(SEL)sel{

  if (sel == NSSelectorFromString(@"eat")){
    //class:给哪个类添加方法
    //SEL:添加哪个方法
    //IMP:方法的实现
    //type:方法的类型(v = void ,@ = id , : = SEL)
    class_addMethod(self,sel,(IMP) eat,"v@:");
  }
}

7 动态添加属性

需求:给NSObject添加属性name。

步骤
  1. 给NSObject添加分类
  2. 在分类中声明属性(分类中添加属性,只会声明set和get方法,不会生成实现,也不会生成下划线的成员属性)
  3. 在分类中添加set和get方法,可以使用全局静态变量(static)来保存属性。
  4. 用runtime添加属性
static NSString* _name;
//给NSObject类添加一个name属性,设置它的set和get方法
-(void)setName:(NSString *)name{
   
   _name = name
  //第一个参数:给哪个类添加属性
  //第二个参数:属性的名称
  //第三个参数:属性值
  //第四个参数:保存策略
  objc_setAssociateObject(self,@"name",name,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

-(NSString *)name{
return _name
  //第一个参数:获得哪个类添加属性
  //第二个参数:属性的名称
  return objc_getAssociateObject(self,@"name");
}

上一篇 下一篇

猜你喜欢

热点阅读