Runtime你应该知道些什么

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

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容