总结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的功能很强大,还需要朋友们多多学习和研究才可以。希望本文对大家有所帮助。