Runtime

2019-12-22  本文已影响0人  努力爬行中的蜗牛
1 Runtime简介
2 Runtime消息机制
 id:谁发送消息
 SEL:发送什么消息
 ((NSObject *(*)(id, SEL))(void *)objc_msgSend)([NSObject class], @selector(alloc));
 xcode6之前,苹果运行使用objc_msgSend.而且有参数提示
 xcode6苹果不推荐我们使用runtime

最终生成消息机制,编译器做的事情
最终代码,需要把当前代码重新编译,用xcode编译器,clang
clang -rewrite-objc main.m 查看最终生成代码

3 Runtime消息机制调用多个参数
- (void)runtime {
    /*
     runtime:方法都是有前缀,谁的事情谁开头
        开发中使用场景:
        需要用到runtime,消息机制
        不得不用runtime消息机制,可以帮我调用私有方法.
     */
    Person *p = objc_msgSend(objc_getClass("Person"), @selector(alloc));
    p = objc_msgSend(p, @selector(init));
    
    objc_msgSend(p, @selector(eat));
}
4 方法调用流程
5 内存5大区
6 Runtime交换方法
#import "UIImage+Exention.h"

#import <objc/message.h>

@implementation UIImage (Exention)
// 把类加载进内存的时候调用,只会调用一次
+ (void)load {
    // 获取类方法
    // 获取哪个类的方法
    // 获取哪个方法
    Method originalMethod = class_getClassMethod(self, @selector(imageNamed:));
    Method currentMethod = class_getClassMethod(self, @selector(ZYX_imageNamed:));
    
    // 交换方法:runtime
    method_exchangeImplementations(originalMethod, currentMethod);
    
}

+ (UIImage *)ZYX_imageNamed:(NSString *)imageName {
    // 这里其实就是调用系统的imageNamed: 方法
    UIImage *image = [self ZYX_imageNamed:imageName];
    NSLog(@"%s",__func__);
    return image;
}
@end
7 Runtime动态添加方法

Runtime(动态添加方法):OC都是懒加载机制,只要一个方法实现了,就会马上添加到方法列表中.

#import "Person.h"
#import <objc/message.h>

@implementation Person
void run(id self, SEL _cmd, NSNumber *meter) {
    NSLog(@"run %@ meter", meter);
}

// 如果调用的该对象的方法没有实现,则会走这个方法
// 任何方法都包含两个隐式参数:self _cmd
// 动态添加方法,处理未实现
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSLog(@"%@",NSStringFromSelector(sel));
    if (sel == NSSelectorFromString(@"run:")) {
        // class: 给哪个类添加方法
        // SEL: 添加哪个方法
        // IMP: 方法实现 => 函数 => 函数入口 => 函数名
        // type: 方法类型
        class_addMethod(self, sel, (IMP)run, "v@:@");
        return YES;
    }
    return [super resolveClassMethod:sel];
}
@end
8 Runtime动态添加属性
#import "NSObject+Extension.h"
#import <objc/message.h>
// 动态添加属性:什么时候需要动态添加属性

// 开发场景
// 给系统的类添加属性的时候,可以使用runtime动态添加属性方法

// 本质:动态添加属性,就是让某个属性与对象产生关联

// 需求:让一个NSObject类 保存一个字符串

// runtime一般都是针对系统的类
@implementation NSObject (Extension)
- (void)setName:(NSString *)name {
    /*
     参数1:给哪个对象添加属性
     参数2:属性名称
     参数3:属性的值
     参数:保存策略
     */
    objc_setAssociatedObject(self, "name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *)name {
    return objc_getAssociatedObject(self, "name");
}

@end
9 自动生成属性代码
#import "NSDictionary+Extension.h"

@implementation NSDictionary (Extension)
// isKindOfClass:判断是否是当前类或者子类
// 生成属性代码 => 根据字典中所有key
- (void)createProperty {
    __block NSMutableString *codes = [NSMutableString string];
    [self enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull value, BOOL * _Nonnull stop) {
//        NSLog(@"%@",NSStringFromClass([value class]));
        NSString *code;
        if ([value isKindOfClass:[NSString class]]) {
            code = [NSString stringWithFormat:@"@property (strong, nonatomic) NSString *%@;",key];
        } else if ([value isKindOfClass:NSClassFromString(@"__NSCFBoolean")]) {
            code = [NSString stringWithFormat:@"@property (assign, nonatomic) BOOL %@;",key];
        }
        else if ([value isKindOfClass:[NSNumber class]]) {
            code = [NSString stringWithFormat:@"@property (assign, nonatomic) NSInteger %@;",key];
        } else if ([value isKindOfClass:[NSArray class]]) {
            code = [NSString stringWithFormat:@"@property (strong, nonatomic) NSArray *%@;",key];
        } else if ([value isKindOfClass:[NSDictionary class]]) {
            code = [NSString stringWithFormat:@"@property (strong, nonatomic) NSDictionary *%@;",key];
        }
        [codes appendFormat:@"\n%@\n",code];
    }];
    NSLog(@"%@",codes);
}
@end
10 Runtime字典转模型

KVC方式:[item setValue:@"来自即刻笔记" forKey:@"source"]:

// 获取类里面所有方法
// class_copyMethodList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>)// 本质:创建谁的对象

// 获取类里面属性
//  class_copyPropertyList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>)

// Ivar:成员变量 以下划线开头
// Property:属性
+ (instancetype)modelWithDict:(NSDictionary *)dict {
    id objc = [[self alloc] init];
    // runtime:根据模型中属性,去字典中取出对应的value给模型属性赋值
    // 1.获取模型中所有成员变量 key
    // 获取哪个类的成员变量
    // count:成员变量个数
    unsigned int count;
    // 获取成员变量数组
    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)];
        // @\"User\" -> User
        [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
        [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
        
        // 获取key
        NSString *key = [ivarName substringToIndex:1];
        id value = dict[key];
        
        // 二级转换:判断下value是否是字典,如果是,字典转换层对应的模型
        // 并且是自定义对象才需要转换
        if ([ivarType isKindOfClass:[NSDictionary class]] && ![ivarType hasPrefix:@"NS"]) {
            Class class = NSClassFromString(ivarType);
            [class modelWithDict:value];
        }
        
        if (value) {
            [objc setValue:value forKey:key];
        }
    }
    return objc;
}
上一篇 下一篇

猜你喜欢

热点阅读