iOS复习之--runtime

318 阅读3分钟

前言

最近在准备面试,所以刷一波底层原理;本文主要介绍runtime相关一些知识点

一、runtime的概念

  • runtime简称运行时,OC就是运行时机制,也就是说在运行时候的一些机制,其中最主要的就是消息机制。

  • 对于C语言,函数的调用在编译时候就已经决定了调用哪个函数

  • OC的函数,属于运行时调用,在编译的时候并不能真正的决定调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。

  • 编译阶段,OC调用任何函数,即使并未实现,只要有声明就不会报错;C语言调用未实现函数会报错;

二、runtime的作用

2.1 消息机制

  • 方法调用的本质: 实际就是让对象发送消息
  • objc_msgSend, 只有对象才能发送消息,因此以objc开头
  • 使用消息机制前提,导入头文件#import<objc/message.h>
  • 消息机制的原理: 对象根据方法编号SEL去方法映射表中查找对应的方法实现

2.2.1消息调用的流程

1.通过对象的isa指针去对应的类中查找
2.注册方法编号
3.根据方法编号去查找对应的方法
4.找到最终函数实现地址,根据地址再去方法去调用对应的函数

2.2 交换方法(黑魔法)

  • 系统自带的方法不能满足需求的时候,可以给系统自带的方法扩展一些功能,并且还能保持原有的功能;

    • 常用的:继承自系统的类,重写方法

    • 高端的:使用runtime,交换方法

    代码实现runtime交换方法:

#import "UIImage+XK_Image.h"
#import <objc/runtime.h>

@implementation UIImage (XK_Image)

// 把类加载进内存的时候调用,并且只会调用一次
+ (void)load{
    
    // 交换方法
    Method imageNameMethod = class_getClassMethod(self, @selector(imageNamed:));
    Method xk_imageNameMethod = class_getClassMethod(self, @selector(xk_imageNamed:));
    
    // 交换方法地址,相当于交换实现方法
    method_exchangeImplementations(imageNameMethod, xk_imageNameMethod);
}

// 特别注意: 不能再分类中重写系统的imageNamed方法,因为会把系统的功能给覆盖掉,而且分类中不能调用super

// 在这的目标就是 能加载图片并且可以完成打印
+ (UIImage *)xk_imageNamed:(NSString *)name{
    
    // 图片
    // 在这里就相当于调用了系统的imageNamed:方法
    UIImage * image = [UIImage xk_imageNamed:name];
    
    if (image) {
        NSLog(@"图片加载成功");
    } else {
        NSLog(@"图片加载失败");
    }
    return image;
}

@end   

在ViewController调用实现:

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    UIImage * image = [UIImage imageNamed:@"image1"];

    UIImageView * img = [[UIImageView alloc] initWithImage:image];
    img.frame = CGRectMake(0, 0, 200, 200);
    [self.view addSubview:img];
    
//    打印结果:
//    2020-09-25 15:45:10.618627+0800 GCDSemephore[3173:139764] 图片加载成功
}

2.3 给分类添加属性

  • tips: 实际开发中,分类是不可以添加属性的,虽然可以用@property声明,也并不会报错,但是他仅仅也是自动生成了get和set的声明,并没有带下划线的属性和方法实现的生成,所以可以通过runtime给他添加方法的实现
  • 原理: 给一个类声明属性,其实本质就是给这个类添加关联,并不是直接把这个值的内存空间添加到类的内存空间
  • 需求: 给系统的NSObject类添加一个name属性
=========NSObject+Dog.m文件===========

#import "NSObject+Dog.h"
#import <objc/runtime.h>

static char const * const dogName = "dogName";
@implementation NSObject (Dog)

- (void)setName:(NSString *)name{
    
    /**
     objc_setAssociatedObject 方法共有四个参数 (将某个值跟某个对象关联起来,将某个值存储到某个对象中)
     第一个参数 id  _Nonnull object: 给哪个对象添加属性
     第二个参数 const void * _Nonnull key:属性名称
     第三个参数 id  _Nullable value:属性值
     第四个参数 objc_AssociationPolicy policy:保存策略
     */
    objc_setAssociatedObject(self, dogName, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
}

- (NSString *)name{
    //objc_getAssociatedObject 获取某个对象的值
    return objc_getAssociatedObject(self, dogName);
}

@end
======ViewController.m 文件调用  ======

 	NSObject * obj = [[NSObject alloc] init];
    obj.name = @"哈士奇";
    NSLog(@"我就是分类中添加的name属性%@",obj.name);
//    打印结果:
//    我就是分类中添加的name属性哈士奇

暂时整理的这些,后续再补充