Runtime是什么?
字面的意思:运行时,是一套API【用C 和汇编写的】,是iOS的核心之一,是OC的底层,举个栗子
[NSObject alloc];
objc_msgSend(NSObject, selector)
OC是一门动态语言,很多类,成员变量以及方法实现,在编译时是不确定的,只有运行时,才能确定。
下面我们介绍一些Runtime的实战经验
1.替换方法
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(perfect);
SEL swizzledSelector = @selector(newPerfect);
Method originalMethod = class_getInstanceMethod(class,originalSelector);
Method swizzledMethod = class_getInstanceMethod(class,swizzledSelector);
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
- (void)newPerfect {
NSLog(@"成功替换方法");
}
- (void)perfect {
NSLog(@"原来的方法");
}
2.动态增加属性
/// 动态增加属性
static const char associatedKey;
- (IBAction)addObject {
PerfectObject *obj = [PerfectObject new];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"我是PerfectObject动态挂载的" message:nil delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
objc_setAssociatedObject(obj, &associatedKey, alert, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self getAssociatedObject:obj];
}
- (void)getAssociatedObject:(PerfectObject *)obj {
UIAlertView *alert = objc_getAssociatedObject(obj, &associatedKey);
[alert show];
}
3.自动归档与解档
/// 动态增加属性
static const char associatedKey;
+ (void)addObject {
PerfectObject *obj = [PerfectObject new];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"我是PerfectObject动态添加的" message:nil delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
objc_setAssociatedObject(obj, &associatedKey, alert, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self getAssociatedObject:obj];
}
+ (void)getAssociatedObject:(PerfectObject *)obj {
UIAlertView *alert = objc_getAssociatedObject(obj, &associatedKey);
[alert show];
}
/// 自动归档与解档
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
unsigned int outCount;
Ivar * ivars = class_copyIvarList([self class], &outCount);
for (int i = 0; i < outCount; i ++) {
Ivar ivar = ivars[i];
NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];
[self setValue:[aDecoder decodeObjectForKey:key] forKey:key];
}
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
unsigned int outCount;
Ivar * ivars = class_copyIvarList([self class], &outCount);
for (int i = 0; i < outCount; i ++) {
Ivar ivar = ivars[i];
NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];
[aCoder encodeObject:[self valueForKey:key] forKey:key];
}
}
+ (void)coding {
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES).lastObject;
NSString *file = [path stringByAppendingPathComponent:@"perfectObject.archiver"];
PerfectObject *object = [[PerfectObject alloc] init];
object.name = @"yangdongzheng";
object.age = @"18";
object.sex = @"男";
BOOL result = [NSKeyedArchiver archiveRootObject:object toFile:file];
if (result) {
NSLog(@"good");
}
PerfectObject *perfect = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
NSLog(@"%@ %@ %@ %@", perfect, perfect.name, perfect.age, perfect.sex);
}
4.实现万能控制器跳转
参见:https://www.jianshu.com/p/00c8ae6b46f1
5.动态增加方法
/// 动态添加方法
+ (void)doSing {
PerfectObject *p = [[PerfectObject alloc] init];
[p performSelector:@selector(sing)];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(sing)) {
Class class = [self class];
SEL singSelector = @selector(sing);
Method singMethod = class_getInstanceMethod(class,singSelector);
class_addMethod(class, singSelector, method_getImplementation(singMethod), method_getTypeEncoding(singMethod));
// 处理完返回YES
return YES;
}
return [super resolveInstanceMethod:sel];
}
- (void)sing {
NSLog(@"新添加了唱歌的方法");
}
6.字典转模型
/// 字典转模型
+ (void)dictionaryToModel {
NSDictionary *studentInfo = @{@"name" : @"Kobe Bryant",
@"age" : @"18",
@"sex" : @"男"};
PerfectObject *student = [[PerfectObject alloc] init];
[student setValuesForKeysWithDictionary:studentInfo];
PerfectObject *student1 = [PerfectObject modelWithDict:studentInfo];
NSLog(@"student = %@,%@,%@,%@",student1, student1.name, student1.age, student1.sex);
}
@implementation NSObject (DictionaryToModel)
/*
* 根据模型中属性,去字典中取出对应的value并赋值给模型的属性
* 遍历取出所有属性
*/
+ (instancetype)modelWithDict:(NSDictionary *)dict {
//1. 创建对应的对象
id objc = [[self alloc] init];
//2. 利用runtime给对象中的属性赋值
/*
Ivar: 成员变量;
class_copyIvarList(): 获取类中的所有成员变量;
第一个参数:表示获取哪个类的成员变量;
第二个参数:表示这个类有多少成员变量;
返回值Ivar *:指的是一个ivar数组,会把所有成员变量放在一个数组中,通过返回数组就全部获取到。
*/
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList(self, &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivarList[i];
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
NSString *key = [ivarName substringFromIndex:1];
id value = dict[key];
if (!value) {
NSDictionary *customKeyDict = [self modelCustomPropertyMapper];
NSString *customKey = customKeyDict[key];
value = dict[customKey];
}
if (value) {
[objc setValue:value forKey:key];
}
}
return objc;
}
+ (instancetype)modelWithDict2:(NSDictionary *)dict {
if (![dict isKindOfClass:[NSDictionary class]]) {
return nil;
}
id objc = [[self alloc] init];
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList(self, &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivarList[i];
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
NSString *key = [ivarName substringFromIndex:1];
id value = dict[key];
if (!value) {
NSDictionary *customKeyDict = [self modelCustomPropertyMapper];
NSString *customKey = customKeyDict[key];
value = dict[customKey];
}
if ([value isKindOfClass:[NSDictionary class]] && ![ivarType hasPrefix:@"NS"]) {
Class modelClass = NSClassFromString(ivarType);
if (modelClass) {
value = [modelClass modelWithDict2:value];
}
}
if ([value isKindOfClass:[NSArray class]]) {
if ([self respondsToSelector:@selector(arrayContainModelClass)]) {
// 转换成id类型,就能调用任何对象的方法
id idSelf = self;
// 获取数组中字典对应的模型
NSString *type = [idSelf arrayContainModelClass][key];
if (type) {
// 生成模型
Class classModel = NSClassFromString(type);
NSMutableArray *arrM = [NSMutableArray array];
// 遍历字典数组,生成模型数组
for (NSDictionary *dict in value) {
// 字典转模型
id model = [classModel modelWithDict2:dict];
if (model) {
[arrM addObject:model];
} else {
//如果数组中的某个元素并不是个字典,则不做解析
[arrM addObject:dict];
}
}
// 把模型数组赋值给value
value = arrM;
}
}
}
if (value) {
[objc setValue:value forKey:key];
}
}
return objc;
}
+ (NSDictionary *)modelCustomPropertyMapper {
return @{};
}
+ (NSDictionary *)arrayContainModelClass {
return @{};
}
7.JSPatch热修复
参考链接:http://www.jspatch.com/Docs/intro
一言不合就上代码
Demo链接:https://github.com/DuffYang/PerfectRuntime