初尝iOS RunTime 运行时

RunTime简介

  • RunTime简称运行时。

  • OC就是运行时机制,就是在运行的时候调用一些机制。

  • 对于C语言,在编译的时候会决定调用哪个函数。

  • 对于OC,OC是一种动态语言,所以在编译的时候并不能决定调用哪个函数,只有在真正运行的时候才会根据函数的SEL来调用对应的函数。

 

也就是说:

 

  • 在编译阶段,OC可以调用任何函数,只要这个函数声明了,就不会报错。

  • 在编译阶段,C语言调用未声明的函数就会报错。

RunTime作用

  1. 发送消息-函数调用机制

    • OC方法调用的本质就是让obj(对象)发送消息。

    • 通过objc_msgSend方法,只有obj(对象)才能发送消息,因此以objc开头。

    • 使用消息机制前提,必须导入#import

     1 @implementation ViewController
     2 
     3 - (void)viewDidLoad {
     4     [super viewDidLoad];
     5  
     6     //创建一个类对象p
     7     Person *p = [[Person alloc] init];
     8 
     9     //一般调用对象方法
    10     [p eat];
    11     //本质上却是让对象P发送消息
    12     objc_msgSend(p, @selector(eat));
    13     
    14     //一般调用类方法
    15     [Person drink];
    16     //也可以通过类对象来调用
    17     [[Person class] drink];
    18     // 用类名调用类方法,底层会自动把类名转换成类对象调用
    19     // 本质:让类对象发送消息
    20     objc_msgSend([Person class], @selector(drink));
    21     
    22     
    23 }

     

     

  2. 交换方法

    • 开发场景:系统自带的方法的功能不够。给系统自带的方法扩充一些方法,并保持原有方法的功能。

    • 解决方案: 可以继承系统的类,重写系统的方法。

         

     1 @implementation ViewController
     2 
     3 - (void)viewDidLoad {
     4     [super viewDidLoad];
     5     // 需求:给imageNamed方法提供功能,每次加载图片就判断下图片是否加载成功。
     6     // 步骤一:先搞个分类,定义一个能加载图片并且能打印的方法+ (instancetype)imageWithName:(NSString *)name;
     7     // 步骤二:交换imageNamed和imageWithName的实现,就能调用imageWithName,间接调用imageWithName的实现。
     8     UIImage *image = [UIImage imageNamed:@"321"];
     9 }
    10 
    11 @implementation UIImage (Image)
    12 + (void)load
    13 {
    14     /*
    15      *交换方法
    16      *1.获取要交换位置的方法地址和被交换方法的地址
    17      *2.通过method_exchangeImplementations(<#Method m1#>, <#Method m2#>)来交换俩方法
    18      */
    19     
    20     //获取imageWithName方法的地址
    21     Method imageWithName = class_getClassMethod(self, @selector(imageWithName:));
    22     
    23     //获取imageName方法的地址
    24     Method imageName = class_getClassMethod(self, @selector(imageNamed:));
    25     
    26     //交换方法地址:交换俩方法的实现方式
    27     method_exchangeImplementations(imageWithName, imageName);
    28     
    29 }
    30 
    31 
    32 //在这里不能实现系统的imageNamed方法,因为会把系统的功能给覆盖掉,而且分类中不能调用super.
    33 + (instancetype)imageWithName:(NSString *)imageName
    34 {
    35     // 这里调用imageWithName,相当于调用imageName
    36     UIImage *image = [self imageWithName:imageName];
    37     
    38     if (image == nil) {
    39         NSLog(@"加载空的图片");
    40     }
    41     return image;
    42 }

     

  3. 动态添加方法
    • 使用场景:一个类的方法非常多,加载到内存的时候也非常的耗费资源,这时候就可以给该类动态创建方法来解决。

    • 面试:有没有使用performSelector,其实主要想问你有没有动态添加过方法。

       1 @implementation ViewController
       2 
       3 - (void)viewDidLoad {
       4     [super viewDidLoad];
       5     
       6     Person *p = [Person new];
       7     
       8    // 注意,在这里就不能使用 [self method:] 进行调用了,因为我们添加的方法是在运行时才执行,而编译器只负责编译时的方法检索,一旦对一个对象没有检索到它的 sleeps 方法,就会报错,所以这里我们使用 performSelector:withObject: 来进行调用,保存,运行。
       9     [p performSelector:@selector(sleeps) withObject:@"221"];
      10 }
      11 
      12 
      13 
      14 @implementation Person
      15 
      1617 // 默认方法都有两个隐式参数,
      18 void sleeps(id self , SEL sel, NSString *brand)
      19 {
      20     NSLog(@"%@",brand);
      21     NSLog(@"%@ %@",self,NSStringFromSelector(sel));
      22 }
      23 
      24 // 当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来.
      25 // 刚好可以用来判断,未实现的方法是不是我们想要动态添加的方法
      26 + (BOOL)resolveInstanceMethod:(SEL)sel
      27 {
      28     if (sel == @selector(sleeps)) {
      29         // 动态添加eat方法
      30         // 第一个参数:给哪个类添加方法
      31         // 第二个参数:添加方法的方法编号
      32         // 第三个参数:添加方法的函数实现(函数地址)
      33         // 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
      34         class_addMethod(self, @selector(sleeps), (IMP)sleeps, "v@:");
      35         
      36        // 解释一下,这个函数在 runtime 环境下,如果没有找到该方法的实现的话就会执行。第一行判断的是传入的 SEL 名称是否匹配,接着调用 class_addMethod 方法,传入相应的参数。其中第三个参数传入的是我们添加的 C 语言函数的实现,也就是说,第三个参数的名称要和添加的具体函数名称一致。第四个参数指的是函数的返回值以及参数内容。
      37         
      38        // 至于该类方法的返回值,在我测试的时候,无论这个 BOOL 值是多少,并不会影响我们的执行目标,一般返回 YES 即可。
      39         
      40     }
      41     
      42     return [super resolveInstanceMethod:sel];
      43 }

       

  4. 动态添加属性
    • 原理:给一个类声明属性,其实本质就是给这个类添加关联,并不是直接把这个值的内存空间添加到类的内存空间。
      @implementation ViewController
      
      - (void)viewDidLoad {
          [super viewDidLoad];
          
          Person *objc = [[Person alloc] init];
          objc.name = @"取名困难户";
          NSLog(@"%@",objc.name);
      }
      
      //类中无任何属性
      #import <Foundation/Foundation.h>
      
      @interface Person : NSObject
      
      @end
      
      #import "Person.h"
      #import <objc/objc-runtime.h>
      @implementation Person
      @end
      
      //建立一个Person的分类
      //在分类中@property不会生成_变量,也不会实现getter和setter方法,我们可以手动实现如下
      //-(NSString *)name{
      //    return @"name";
      //}
      //-(void)setName:(NSString *)name{
      //    
      //}
      //但是这样是没什么意义的,而且分类中不允许定义变量,所以只能用runtime类实现
      
      //定义常量 必须是C语言字符串
      static const char *key = "kname";
      @implementation Person (text)
      
      - (NSString *)name
      {
          // 根据关联的key,获取关联的值。
          return objc_getAssociatedObject(self, key);
      }
      
      - (void)setName:(NSString *)name
      {
          
          /*
           OBJC_ASSOCIATION_ASSIGN;            //assign策略
           OBJC_ASSOCIATION_COPY_NONATOMIC;    //copy策略
           OBJC_ASSOCIATION_RETAIN_NONATOMIC;  // retain策略
           
           OBJC_ASSOCIATION_RETAIN;
           OBJC_ASSOCIATION_COPY;
           */
          /*
           * id object 给哪个对象的属性赋值
           const void *key 属性对应的key
           id value  设置属性值为value
           objc_AssociationPolicy policy  使用的策略,是一个枚举值,和copy,retain,assign是一样的,手机开发一般都选择NONATOMIC
           objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
           */
          
          // 第一个参数:给哪个对象添加关联
          // 第二个参数:关联的key,通过这个key获取
          // 第三个参数:关联的value
          // 第四个参数:关联的策略
          objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
      }

       

  5. 字典转模型
    • 可以使用kvc来字典转模型

    • 使用runtime字典转模型

    思路:利用运行时,遍历模型中所有属性,根据模型的属性名,去字典中查找key,取出对应的值,给模型的属性赋值。

 

posted @ 2017-06-21 17:49  搬砖工1号  阅读(172)  评论(0编辑  收藏  举报