前语

在平日的iOS开发中,最常见的的用到runtime的应该便是分类增加特点了,咱们平常在类里运用@property增加特点的时分,体系会自动生成带“_”做前缀的的成员变量以及该变量的setter和getter办法。但是在分类里运用@property增加特点,.m文件会出现正告,提示没有完成改特点的的setter和getter办法。需求咱们手动增加代码,并且手动增加的代码也无法用“_”做前缀+特点变量名去访问特点,需求咱们动态runtime的代码去完成。那么runtime是什么?runtime的办法又有哪些?作为进阶学习,咱们应该要把握哪些常用的runtime代码,接下来逐个讲解。

一、什么是runtime

浅显来说,RunTime其实便是一个库,是oc底层的C言语API,咱们平常写的oc代码,其实最终都会被编译器转化为runtime代码。
runtime顾名思义便是运转时的意思,即咱们运用runtime的代码,体系并不是编译的时分去履行,而是运转中去到该代码处才会去动态履行这些代码。像是一些变量的值,或许依据需求咱们得在程序运转到某一个过程的时分才干确认它的值,然后取出来,这时分则能够运用runtime的代码在对应的那个时刻去获取变量的值,然后动态的做一些操作。

二、runtime的办法

运用RunTime库里的代码,需求引进头文件

#import <objc/runtime.h>

有些函数则需求引进

#import <objc/message.h>

点击进入头文件,能够看到runtime提供的办法
下图仅仅其中一部分,其他自己能够去头文件自己阅读

iOS |iOS进阶必须掌握的常用的runtime方法

三、runtime需求把握的办法

为啥咱们需求学习runtime?我认为有以下几点
  1.runtime能做一些咱们用oc代码比较难完成的功能,例如阻拦某个办法,然后进行办法交流(一半是阻拦交流体系办法)
  2.咱们平常oc的函数调用,实践最后都会变成runtime的音讯转发机制,把握这个音讯转发机制,咱们就能够把音讯重定向到其他目标,然后写出更加灵敏的代码,
  3.能运用体系动态调用函数这个机制去动态履行一些操作,比方新增一个目标,增加一个办法or一个特点,替换掉办法等。


runtime有哪些作为进阶需求把握的办法?
1.给分类增加特点
比方我想给某个SDK里的目标增加特点,但是无法修正这个目标,这时分就能够用分类来完成
这儿需求用到相关函数objc_setAssociatedObjectobjc_getAssociatedObject,其定义如下

iOS |iOS进阶必须掌握的常用的runtime方法
函数objc_setAssociatedObject的参数意义如下
第一个参数:表明给哪个目标增加相关
第二个参数:表明相关的key,后边要获取的objc_getAssociatedObject则是通过这个key去获取值
第三个参数:表明这个key对应的值,第四个参数则是这个
第四个参数:policy表明相关战略,是一个枚举,现在有五种战略

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
  OBJC_ASSOCIATION_ASSIGN = 0,     
  OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, 
  OBJC_ASSOCIATION_COPY_NONATOMIC = 3,  
  OBJC_ASSOCIATION_RETAIN = 01401,   
  OBJC_ASSOCIATION_COPY = 01403     
};

等效于

iOS |iOS进阶必须掌握的常用的runtime方法

而函数objc_getAssociatedObject的参数意义如下
第一个参数:表明相关的目标
第二个参数:表明相关的key,返回值便是上面办法中相同key的value目标

运用办法如下: 先创立一个NSSObjcet的分类

iOS |iOS进阶必须掌握的常用的runtime方法
iOS |iOS进阶必须掌握的常用的runtime方法

然后为分类增加特点name,然后运用runtime代码设置name的gettersetter办法

NSObject+myobj.h

#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSObject (myobj)
@proprery (nonatomic, strong) NSString *name;
@end
NS_ASSUME_NONNULL_END

NSObject+myobj.m

#import "NSObject+myobj.h"
#import <objc/runtime.h>
static NSString * const key = @"myName";
@implementation** NSObject (myobj)
- (NSString *)name
{
  return objc_getAssociatedObject(self, &key);
}
- (void)setName:(NSString *)name
{
  objc_setAssociatedObject(self, &key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

这样,就能给生成的NSObject实例增加姓名和读取姓名了

NSObject *objc = [[NSObject alloc] init];
objc.name = @"hello world";
NSLog(@"%@",objc.name);


2.交流办法
假如我想打印一下用户的app运用路径,我想每个VC的appear和disappear相关函数调用时都打印一下,那么就能够阻拦体系的这些办法,然后用我写好的办法进行交流完成
这儿需求用到
获取办法函数class_getInstanceMethod
交流函数method_exchangeImplementations
其定义如下

iOS |iOS进阶必须掌握的常用的runtime方法

iOS |iOS进阶必须掌握的常用的runtime方法

运用办法如下:
先创立一个UIViewController的分类
iOS |iOS进阶必须掌握的常用的runtime方法
然后在UIViewController+mylog.m文件里写入下面的代码

#import "UIViewController+mylog.h"
#import <objc/runtime.h>
@implementation UIViewController (mylog)
+(void)load{
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    //交流appear
    Method org_viewDidAppear = class_getInstanceMethod(**self**, **@selector**(viewDidAppear:));
    Method new_viewDidAppear = class_getInstanceMethod(**self**, **@selector**(myViewDidAppear:));
    method_exchangeImplementations(org_viewDidAppear, new_viewDidAppear);
 
    //交流disappear
    Method org_viewDidDisappear = class_getInstanceMethod(**self**, **@selector**(viewDidDisappear:));
    Method new_viewDidDisappear = class_getInstanceMethod(**self**, **@selector**(myViewDidDisappear:));
    method_exchangeImplementations(org_viewDidDisappear, new_viewDidDisappear);
  });
}
- (void)myViewDidAppear:(BOOL)animated{
  NSLog(@"%@ viewDidAppear",self);
  [self myViewDidAppear:animated];//重定向回到vc的viewDidAppear办法
}
- (void)myViewDidDisappear:(BOOL)animated{
  NSLog(@"%@ viewDidDisappear",self);
  [self myViewDidDisappear:animated];//重定向回到vc的viewDidDisappear办法
}
@end

四、总结

关于runtime代码的运用,我只列出了现在最常用的2个,其实还有很多用法,大家能够上网查找学习一下,有空我也会弥补本文,另外我也会找时刻写一下我对runtime运转时音讯转发流程的了解,谢谢。