关于runtime简单来说就是运行时,是系统运行的一些机制,其中最主要的是消息机制。
example:
OC里面调用方法:
[objc setName];
在编译的时候runtime会将上述代码转换成:objc_msgSend(objc,@seletor(setName));
更详细的定义网上有许多,这里就不详细介绍,这里主要介绍几个常用的方法。
一、 数据的转换(字典到model)
class_copyPropertyList可以获取当前类的属性
///通过运行时获取当前对象的所有属性的名称,以数组的形式返回
- (NSArray *) allPropertyNames{
///存储所有的属性名称
NSMutableArray *allNames = [[NSMutableArray alloc] init];
///存储属性的个数
unsigned int propertyCount = 0;
///通过运行时获取当前类的属性
objc_property_t *propertys = class_copyPropertyList([self class], &propertyCount);
//把属性放到数组中
for (int i = 0; i < propertyCount; i ++) {
///取出第一个属性
objc_property_t property = propertys[i];
const char * propertyName = property_getName(property);
[allNames addObject:[NSString stringWithUTF8String:propertyName]];
}
///释放
free(propertys);
return allNames;
}
/// 通过字符串来创建该字符串的Setter方法,并返回
- (SEL) creatSetterWithPropertyName: (NSString *) propertyName{
//1.首字母大写
propertyName = propertyName.capitalizedString;
//2.拼接上set关键字
propertyName = [NSString stringWithFormat:@"set%@:", propertyName];
//3.返回set方法
return NSSelectorFromString(propertyName);
}
/// 把字典赋值给当前实体类的属性
-(void) assginToPropertyWithDictionary: (NSDictionary *) data{
if (data == nil) {
return;
}
///1.获取字典的key
NSArray *dicKey = [data allKeys];
///2.循环遍历字典key, 并且动态生成实体类的setter方法,把字典的Value通过setter方法
///赋值给实体类的属性
for (int i = 0; i < dicKey.count; i ++) {
///属性的存在
if ([[self allPropertyNames]containsObject:dicKey[i]]) {
///2.1 通过getSetterSelWithAttibuteName 方法来获取实体类的set方法
SEL setSel = [self creatSetterWithPropertyName:dicKey[i]];
if ([self respondsToSelector:setSel]) {
///2.2 获取字典中key对应的value
NSString *value = [NSString stringWithFormat:@"%@", data[dicKey[i]]];
///2.3 把值通过setter方法赋值给实体类的属性
[self performSelectorOnMainThread:setSel
withObject:value
waitUntilDone:[NSThread isMainThread]];
}
}
}
}
- 思考
上面的代码是在model的成员变量的名称和字典的key一致的情况下,如何实现不一致的情况?(一一映射)
二、 归档
实现NSCoding协议,class_copyIvarList可以获取当前类的成员变量
- (void)encodeWithCoder:(NSCoder *)aCoder {
Class c = self.class;
// 截取类和父类的成员变量
while (c && c != [NSObject class]) {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(c, &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
id value = [self valueForKey:key];
[aCoder encodeObject:value forKey:key];
}
c = [c superclass];
// 释放内存
free(ivars);
}
}
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder{
self = [super init];
if (self) {
Class c = self.class;
// 截取类和父类的成员变量
while (!c && c != [NSObject class]) {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([self class], &count);
for (int i=0; i<count; i++) {
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
id value = [aDecoder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
c = [c superclass];
free(ivars);
}
}
return self;
}
三、 关联对象
分类不可以创建属性,但是可以通过运行时来设置成员变量
static NSString *value = nil;
//设置关键key
static const char associatedkey;
//保存value值
value = @"test";
objc_setAssociatedObject(self, &associatedkey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//通过 objc_getAssociatedObject 获取关联对象
value = objc_getAssociatedObject(self, &associatedkey);
四、 方法替换
增加一个分类,在分类中实现:
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(xxx_viewWillAppear:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// 如果 swizzling 的是类方法, 采用如下的方式:
// Class class = object_getClass((id)self);
// ...
// Method originalMethod = class_getClassMethod(class, originalSelector);
// Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
//交换实现
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
///Method Swizzling
- (void)xxx_viewWillAppear:(BOOL)animated {
[self xxx_viewWillAppear:animated];
NSLog(@"viewWillAppear: %@", self);
}
应用场景:一个项目,替换所有图片,但是图片多了一个后缀“_new”,可以替换imageName方法;埋点;
五、 方法拦截
方法拦截并转发