Runtime

作者: 努力爬行中的蜗牛 | 来源:发表于2019-12-22 08:57 被阅读0次
1 Runtime简介
  • RunTime简称运行时。OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制。

  • 对于C语言,函数的调用在编译的时候会决定调用哪个函数。

  • 对于OC的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。

  • 事实证明:
    1)在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。
    2)在编译阶段,C语言调用未实现的函数就会报错。

2 Runtime消息机制
  • 类方法本质:类对象调用[NSObject class]
 id:谁发送消息
 SEL:发送什么消息
 ((NSObject *(*)(id, SEL))(void *)objc_msgSend)([NSObject class], @selector(alloc));
 xcode6之前,苹果运行使用objc_msgSend.而且有参数提示
 xcode6苹果不推荐我们使用runtime
  • 解决消息机制方法提示步骤
    查找build setting -> 搜索msg

最终生成消息机制,编译器做的事情
最终代码,需要把当前代码重新编译,用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 方法调用流程
  • 怎么去调用eat方法 ,对象方法:类对象的方法列表 类方法:元类中方法列表
  • 1.通过对象的isa去对应的类对象中查找
  • 2.注册方法编号
  • 3.根据方法编号去查找对应方法
  • 4.找到只是最终函数实现地址,根据地址去方法区调用对应函数
5 内存5大区
  • 1.栈 2.堆 3.静态区 4.常量区 5.方法区

  • 1) 栈:不需要手动管理内存,自动管理

  • 2) 堆,需要手动管理内存,自己去释放


    Snip20191220_1.png
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都是懒加载机制,只要一个方法实现了,就会马上添加到方法列表中.

  • app:免费版,收费版
  • QQ,微博,直播等等应用,都有会员机制
#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"]:

  • 首先去模型中查找有没有setSource,找到,直接调用赋值 [self setSource:@"来自即刻笔记"]
  • 去模型中查找有没有source属性,有,直接访问属性赋值 source = value
  • 去模型中查找有没有_source属性,有,直接访问属性赋值 _source = value
  • 找不到,就会直接报错 setValue:forUndefinedKey:报找不到的错误
// 获取类里面所有方法
// 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;
}

相关文章

网友评论

      本文标题:Runtime

      本文链接:https://www.haomeiwen.com/subject/ljsbnctx.html