做iOS的朋友都知道或听说
runtime,这个东西很像
java的反射机制,但功能远胜于
java的反射。通过
runtime我们可以动态的向一个类中
添加属性、成员变量、方法,以及对其进行读写访问。
一、runtime简介RunTime简称运行时。OC就是
运行时机制,也就是在运行时候 的一些机制,其中最主要的是消息机制。
对于C语言,
函数的调用在编译的时候会决定调用哪个函数。
对于OC的函数,属于
动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称
找到对应的函数来调用。
事实证明:
在编译阶段,OC可以
调用任何函数,即使这个函数并未实现,只要声明过就不会报错。
在编译阶段,C语言调用
未实现的函数就会报错。
二、runtime作用1.发送消息方法调用的本质,就是让对象发送消息。
objc_msgSend,
只有对象才能发送消息,因此以objc开头.
使用消息机制前提,必须导入
#import <objc/message.h>
消息机制简单使用
消息机制原理:对象根据方法编号SEL去映射表查找对应的方法实现
// 创建person对象Person *p = [[Person alloc] init];// 调用对象方法[p eat];// SEL:方法编号,根据方法编号就可以找到对应方法实现[p performSelector:@selector(eat)];// 本质:让对象发送消息objc_msgSend(p, @selector(eat));// 调用类方法的方式:两种// 第一种通过类名调用本质类名转换成类对象[Person eat];// 第二种通过类对象调用[[Person class] eat];[personClass performSelector:@selector(eat)];// 用类名调用类方法,底层会自动把类名转换成类对象调用// 本质:让类对象发送消息objc_msgSend([Person class], @selector(eat));
2.交换方法开发使用场景:系统自带的方法功能不够,给系统自带的方法扩展一些功能,并且保持原有的功能。
方式一:继承系统的类,重写方法.
方式二:使用runtime,交换方法.
@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view, typically from a nib.// 需求:给imageNamed方法提供功能,每次加载图片就判断下图片是否加载成功。// 步骤一:先搞个分类,定义一个能加载图片并且能打印的方法+ (UIImage *)xmg_imageNamed:(NSString *)imageName;// 步骤二:交换imageNamed和xmg_imageNamed的实现,就能调用xmg_imageNamed,间接调用xmg_imageNamed的实现。UIImage *image = [UIImage imageNamed:@"123"]; imageNamed: 实现方法:底层调用PH_imageNamed 本质:交换两个方法的实现imageNamed和PH_imageNamed方法 调用imageNamed其实就是调用PH_imageNamed imageNamed加载图片,并不知道图片是否加载成功 以后调用imageNamed的时候,就知道图片是否加载}@end@implementation UIImage (Image)// 加载分类到内存的时候调用+ (void)load{// 交换方法实现,方法都是定义在类里面// class_getMethodImplementation:获取方法实现// class_getInstanceMethod:获取对象// class_getClassMethod:获取类方法// IMP:方法实现// imageNamed// Class:获取哪个类方法// SEL:获取方法编号,根据SEL就能去对应的类找方法Method imageNameMethod = class_getClassMethod([UIImage class], @selector(imageNamed:));Method PH_imageNameMethod = class_getClassMethod([UIImage class], @selector(PH_imageNamed:));// 交换方法实现method_exchangeImplementations(imageNameMethod, PH_imageNameMethod);} }// 不能在分类中重写系统方法imageNamed,因为会把系统的功能给覆盖掉,而且分类中不能调用super.// 既能加载图片又能打印+ (UIImage *)PH_imageNamed:(NSString *)imageName{// 加载图片UIImage *image = [UIImage PH_imageNamed:imageName];// 2.判断功能if (image == nil) {NSLog(@"加载为空");}return image;}@end
3.动态添加方法开发使用场景:如果一个类方法非常多,加载类到内存的时候也比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法解决。
简单使用@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view, typically from a nib.Person *p = [[Person alloc] init];// 默认person,没有实现eat方法,可以通过performSelector调用,但是会报错。// 动态添加方法就不会报错[p performSelector:@selector(eat)];}@end@implementation Person// void(*)()// 默认方法都有两个隐式参数,默认一个方法都有两个参数,self,_cmd,隐式参数 self:方法调用者 _cmd:调用方法的编号void eat(id self,SEL sel){NSLog(@"%@ %@",self,NSStringFromSelector(sel));}// 当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来.// 刚好可以用来判断,未实现的方法是不是我们想要动态添加的方法<!--动态添加方法,首先实现这个resolveInstanceMethod--><!-- resolveInstanceMethod调用:当调用了没有实现的方法没有实现就会调用resolveInstanceMethod--><!-- resolveInstanceMethod作用:就知道哪些方法没有实现,从而动态添加方法--><!-- sel:没有实现方法-->+ (BOOL)resolveInstanceMethod:(SEL)sel{if (sel == @selector(eat)) {// 动态添加eat方法// 第一个参数:给哪个类添加方法// 第二个参数:添加方法的方法编号// 第三个参数:添加方法的函数实现(函数地址)// 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmdclass_addMethod(self, @selector(eat), eat, "v@:");}return [super resolveInstanceMethod:sel];}@end
4.给分类添加属性原理:给一个类声明属性,其实本质就是给这个类添加关联,并不是直接把这个值的内存空间添加到类存空间。
@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view, typically from a nib.// 给系统NSObject类动态添加属性nameNSObject *objc = [[NSObject alloc] init];objc.name = @"abc";NSLog(@"%@",objc.name);}@end// 定义关联的keystatic const char *key = "name";- (void)setName:(NSString *)name{// 添加属性,跟对象// 给某个对象产生关联,添加属性// object:给哪个对象添加属性// key:属性名,根据key去获取关联的对象 ,void * == id// value:关联的值// policy:策略objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}- (NSString *)name{return objc_getAssociatedObject(self, @"name");}
以上就是iOS中runtime的使用总结,本篇文章主要是原理和用法总结,runtime的功能很强大,还需要朋友们多多学习和研究才可以。希望本文对大家有所帮助。