总结iOS中runtime的使用
做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=[[Personalloc]init]; //调用对象方法 [peat]; //SEL:方法编号,根据方法编号就可以找到对应方法实现 [pperformSelector:@selector(eat)]; //本质:让对象发送消息 objc_msgSend(p,@selector(eat)); //调用类方法的方式:两种 //第一种通过类名调用本质类名转换成类对象 [Personeat]; //第二种通过类对象调用 [[Personclass]eat]; [personClassperformSelector:@selector(eat)]; //用类名调用类方法,底层会自动把类名转换成类对象调用 //本质:让类对象发送消息 objc_msgSend([Personclass],@selector(eat));
2.交换方法
开发使用场景:系统自带的方法功能不够,给系统自带的方法扩展一些功能,并且保持原有的功能。
方式一:继承系统的类,重写方法.
方式二:使用runtime,交换方法.
@implementationViewController -(void)viewDidLoad{ [superviewDidLoad]; //Doanyadditionalsetupafterloadingtheview,typicallyfromanib. //需求:给imageNamed方法提供功能,每次加载图片就判断下图片是否加载成功。 //步骤一:先搞个分类,定义一个能加载图片并且能打印的方法+(UIImage*)xmg_imageNamed:(NSString*)imageName; //步骤二:交换imageNamed和xmg_imageNamed的实现,就能调用xmg_imageNamed,间接调用xmg_imageNamed的实现。 UIImage*image=[UIImageimageNamed:@"123"]; imageNamed: 实现方法:底层调用PH_imageNamed 本质:交换两个方法的实现imageNamed和PH_imageNamed方法 调用imageNamed其实就是调用PH_imageNamed imageNamed加载图片,并不知道图片是否加载成功 以后调用imageNamed的时候,就知道图片是否加载 } @end @implementationUIImage(Image) //加载分类到内存的时候调用 +(void)load { //交换方法实现,方法都是定义在类里面 //class_getMethodImplementation:获取方法实现 //class_getInstanceMethod:获取对象 //class_getClassMethod:获取类方法 //IMP:方法实现 //imageNamed //Class:获取哪个类方法 //SEL:获取方法编号,根据SEL就能去对应的类找方法 MethodimageNameMethod=class_getClassMethod([UIImageclass],@selector(imageNamed:)); MethodPH_imageNameMethod=class_getClassMethod([UIImageclass],@selector(PH_imageNamed:)); //交换方法实现 method_exchangeImplementations(imageNameMethod,PH_imageNameMethod); } } //不能在分类中重写系统方法imageNamed,因为会把系统的功能给覆盖掉,而且分类中不能调用super. //既能加载图片又能打印 +(UIImage*)PH_imageNamed:(NSString*)imageName { //加载图片 UIImage*image=[UIImagePH_imageNamed:imageName]; //2.判断功能 if(image==nil){ NSLog(@"加载为空"); } returnimage; } @end
3.动态添加方法
开发使用场景:如果一个类方法非常多,加载类到内存的时候也比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法解决。
简单使用
@implementationViewController -(void)viewDidLoad{ [superviewDidLoad]; //Doanyadditionalsetupafterloadingtheview,typicallyfromanib. Person*p=[[Personalloc]init]; //默认person,没有实现eat方法,可以通过performSelector调用,但是会报错。 //动态添加方法就不会报错 [pperformSelector:@selector(eat)]; } @end @implementationPerson //void(*)() //默认方法都有两个隐式参数, 默认一个方法都有两个参数,self,_cmd,隐式参数 self:方法调用者 _cmd:调用方法的编号 voideat(idself,SELsel) { NSLog(@"%@%@",self,NSStringFromSelector(sel)); } //当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来. //刚好可以用来判断,未实现的方法是不是我们想要动态添加的方法 <!--动态添加方法,首先实现这个resolveInstanceMethod--> <!--resolveInstanceMethod调用:当调用了没有实现的方法没有实现就会调用resolveInstanceMethod--> <!--resolveInstanceMethod作用:就知道哪些方法没有实现,从而动态添加方法--> <!--sel:没有实现方法--> +(BOOL)resolveInstanceMethod:(SEL)sel { if(sel==@selector(eat)){ //动态添加eat方法 //第一个参数:给哪个类添加方法 //第二个参数:添加方法的方法编号 //第三个参数:添加方法的函数实现(函数地址) //第四个参数:函数的类型,(返回值+参数类型)v:void@:对象->self:表示SEL->_cmd class_addMethod(self,@selector(eat),eat,"v@:"); } return[superresolveInstanceMethod:sel]; } @end
4.给分类添加属性
原理:给一个类声明属性,其实本质就是给这个类添加关联,并不是直接把这个值的内存空间添加到类存空间。
@implementationViewController -(void)viewDidLoad{ [superviewDidLoad]; //Doanyadditionalsetupafterloadingtheview,typicallyfromanib. //给系统NSObject类动态添加属性name NSObject*objc=[[NSObjectalloc]init]; objc.name=@"abc"; NSLog(@"%@",objc.name); } @end //定义关联的key staticconstchar*key="name"; -(void)setName:(NSString*)name { //添加属性,跟对象 //给某个对象产生关联,添加属性 //object:给哪个对象添加属性 //key:属性名,根据key去获取关联的对象,void*==id //value:关联的值 //policy:策略 objc_setAssociatedObject(self,@"name",name,OBJC_ASSOCIATION_RETAIN_NONATOMIC); } -(NSString*)name { returnobjc_getAssociatedObject(self,@"name"); }
以上就是iOS中runtime的使用总结,本篇文章主要是原理和用法总结,runtime的功能很强大,还需要朋友们多多学习和研究才可以。希望本文对大家有所帮助。