1.什么是runtime?
runtime:运行时机制,也是一种消息机制,是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API。
在我们编写的OC语言中,程序运行时,也是通过runtime转化为C语言代码进行编译的,
例如:
// 把Person *p = [[Person alloc]init]分解
Person *p = [Person alloc];
Person *p = objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"));
p = [p init];
p = objc_msgSend(p, @selector(init));
2.runtime有什么作用?
runtime属于OC的底层操作,可以实现使用OC语法无法实现的方法和功能:
- 获取一个类所有的方法,包含私有方法
// 获取类中的方法
- (void)getMethodList
{
int a = 0;
int b = 10;
int c = 2;
int arr[] = {a,b,c};
int *p;
p = arr;
NSLog(@"%d %d",arr[1],p[1]);
// 获取Person类中所有方法
// 参数一:获取哪个类的方法列表
// 参数二:方法列表总数
unsigned int count = 0;
// 调用完这个方法,count就有值,记录方法列表总数
// 获取仅仅是当前类
// 返回指向方法列表数组
Method *methodList = class_copyMethodList([Person class], &count);
// 2 0 1
for (int i = 0; i < count; i++) {
// 取出对应的方法
Method method = methodList[i];
// 获取方法名(方法编号)
SEL methodSel = method_getName(method);
NSLog(@"%@",NSStringFromSelector(methodSel));
}
}
获取类所有的属性,包含私有属性
其实打断点也可以获取一个类的所有属性方法交换
// 加载分类进内存的时候调用
+ (void)load
{
NSLog(@"%s",__func__);
// 交换imageNamed和yc_imageNamed实现
// 获取imageNamed方法
// Class:获取哪个类方法
// SEL:获取方法方法编号
Method imageNameMethod = class_getClassMethod(self, @selector(imageNamed:));
// 获取yc_imageNamed方法
Method yc_imageNameMethod = class_getClassMethod(self, @selector(yc_imageNamed:));
// 交换方法实现
method_exchangeImplementations(imageNameMethod, yc_imageNameMethod);
}
+ (instancetype)yc_imageNamed:(NSString *)name
{
// 加载图片
UIImage *image = [UIImage yc_imageNamed:name];
if (image == nil) {
NSLog(@"加载失败");
}else{
NSLog(@"加载成功");
}
return image;
}
- 动态添加方法
#import "Person.h"
#import
@implementation Person
// 没有返回值,没有参数
// void(*)()
// 完整方法实现
void eat(id self, SEL _cmd, NSString *str)
{
NSLog(@"动态添加的吃方法 %@",str);
}
// 注意:任何一个方法都有两个隐式参数,self,_cmd
// 动态添加方法步骤
// 作用:处理未实现方法
// 什么时候调用:调用了一个未实现的方法就会调用这个方法
// 参数:未实现方法的方法编号
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSLog(@"%@",NSStringFromSelector(sel));
// 动态添加eat
if (sel == @selector(eat:)) {
// 动态添加方法
// Class:给哪个类添加方法
// SEL:添加什么方法
// IMP:方法实现,函数入口 -> 函数名
// type:方法类型 v:void @:id :->SEL
class_addMethod(self, sel, (IMP)eat, "v@:@");
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
- 动态添加属性
#import "NSObject+Property.h"
#import
@implementation NSObject (Property)
- (void)setName:(NSString *)name
{
// name保存到对应对象
// 动态添加属性
// object:给那个对象添加属性
// key:属性名
// value:把什么对象保存起来
// policy:策略,用什么策略
objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)name
{
return objc_getAssociatedObject(self, @"name");
}
@end
- 其他功能
如字典转模型,利用runtime遍历模型对象的所有属性, 根据属性名从字典中取出对应的值, 设置到模型的属性上
大家所熟知的MJExtension框架就大量使用了runtime
3.runtime使用
- 头文件
// 使用runtime需要导入其头文件
#import
// 或者导入其父类
#import
- 相关函数
// 以class_ 开头,这里就不一一列举了
// Ivar:成员变量 Method:成员方法
class_addIvar(<#__unsafe_unretained Class cls#>, <#const char *name#>, <#size_t size#>, <#uint8_t alignment#>, <#const char *types#>)
class_getName(<#__unsafe_unretained Class cls#>)
class_addMethod(<#__unsafe_unretained Class cls#>, <#SEL name#>, <#IMP imp#>, <#const char *types#>)
class_getVersion(<#__unsafe_unretained Class cls#>)
class_addProperty(<#__unsafe_unretained Class cls#>, <#const char *name#>, <#const objc_property_attribute_t *attributes#>, <#unsigned int attributeCount#>)
4.应用实例
iOS-利用runtime修改TextField的占位文字颜色