Runtime

runtime是一套比较底层的纯C语言API,属于1个C语言库,包含了很多底层的C语言API。平时编写的OC代码,在程序运行过程中,其实最终都是转成了runtime的C语言代码,runtime算是OC的幕后工作者。它可以帮助我们实现以下功能:

1. 发送消息

    // 创建person对象
    Person *p = [[Person alloc] init];

    // 调用对象方法
    [p eat];

    // 本质:让对象发送消息
    objc_msgSend(p, @selector(eat));

    // 调用类方法的方式:两种
    // 第一种通过类名调用
    [Person eat];
    // 第二种通过类对象调用
    [[Person class] eat];

    // 用类名调用类方法,底层会自动把类名转换成类对象调用
    // 本质:让类对象发送消息
    objc_msgSend([Person class], @selector(eat));

根据对象的isa找到类中方法编号SEL, 去映射表查找对应的方法名, 根据方法名找到方法区中的方法实现


交换方法

开发使用场景:系统自带的方法功能不够,给系统自带的方法扩展一些功能,并且保持原有的功能。

需求:给imageNamed方法提供功能,每次加载图片就判断下图片是否加载成功。

// 加载类的时候调用,肯定只会调用一次
+ (void)load
{
    // 交互方法实现ozh_imageNamed,imageNamed
    
    // 获取方法 Method:方法名
    // 获取类方法
    // class:获取哪个类方法
    // SEL:方法编号
   Method imageNameMethod = class_getClassMethod(self, @selector(imageNamed:));
    
    Method ozh_imageNameMethod = class_getClassMethod(self, @selector(ozh_imageNamed:));
    
    method_exchangeImplementations(imageNameMethod, ozh_imageNameMethod);
    
}

------------------------------华丽的分割线---------------------------------
//新增的方法
+ (UIImage *)ozh_imageNamed:(NSString *)name
{
    
   UIImage *image = [UIImage ozh_imageNamed:name];
    
    if (image == nil) {
        NSLog(@"加载失败");
    }
    
    return image;
    
}

runtime1.png


2. 动态添加方法

为什么动态添加方法?

例子:

viewController调用了Person类中只有声明没有实现的方法,会报错。

- [p performSelector:@selector(eat)];

解决方案:动态添加方法

@implementation Person

// 定义函数
// 没有返回值,没有参数
// 默认OC方法都有两个隐式参数,self,_cmd
void eat(id self, SEL _cmd) {
    NSLog(@"吃东西");
}

// `resolveInstanceMethod:`什么时候调用:只要调用了有声明没有实现的方法 就会调用该方法去解决
// 作用:我们可以在该方法内`class_addMethod`, 这样便实现了动态添加方法
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    /*
        class:给谁添加方法
        SEL:添加哪个方法
        IMP:方法实现,函数入口,函数名(上面定义的那个东东)
        type:方法类型
     */
    if (sel == @selector(eat)) {
        
        class_addMethod(self, sel, (IMP)eat, "v@:");
        
        return YES;
    }
    
    
    return [super resolveInstanceMethod:sel];
}

3. 动态添加属性

步骤

@implementation NSObject (Property)

//static  NSString *_name;
// 只要想调用runtime方法,思考:谁的事情
- (void)setName:(NSString *)name
{

    // 保存name
    // 动态添加属性 = 本质:让对象的某个属性与值产生关联
    /*
        object:保存到哪个对象中 
        key:用什么属性保存 属性名
        value:保存值
        policy:策略,strong,weak
     */
    objc_setAssociatedObject(self, "name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
   
    
//    _name = name;
}

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

@end

4. 字典转模型

传统字典转模型如果使用KVC方法, 必须要保证 模型中属性名要跟字典中key一一对应

- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
    //不实现该方法, 所以不会报错
}

提供一个NSDictionary的分类以便自动生成属性代码

- (void)createPropetyCode
{
    // 模型中属性根据字典的key
    // 有多少个key,生成多少个属性
    
    NSMutableString *codes = [NSMutableString string];
    // 遍历字典
    [self enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull value, BOOL * _Nonnull stop) {
        NSString *code = nil;
        
        if ([value isKindOfClass:[NSString class]]) {
          code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSString *%@;",key];
        } else if ([value isKindOfClass:NSClassFromString(@"__NSCFBoolean")]){
            code = [NSString stringWithFormat:@"@property (nonatomic, assign) BOOL %@;",key];
        } else if ([value isKindOfClass:[NSNumber class]]) {
             code = [NSString stringWithFormat:@"@property (nonatomic, assign) NSInteger %@;",key];
        } else if ([value isKindOfClass:[NSArray class]]) {
            code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSArray *%@;",key];
        } else if ([value isKindOfClass:[NSDictionary class]]) {
            code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSDictionary *%@;",key];
        }
        
        // 拼接字符串
        [codes appendFormat:@"\n%@\n",code];

    }];
    
    NSLog(@"%@",codes);
    
}

提供一个NSObject的分类以便字典转模型

@property (nonatomic, assign) NSInteger reposts_count;
//reposts_count->属性(property)
//_reposts_count->成员变量(ivar)
+ (instancetype)modelWithDict:(NSDictionary *)dict
{
    id objc = [[self alloc] init];
    
    // 1.获取模型中所有属性 -> 保存到类
    // ivar:成员变量 和 Property:属性
    // 获取成员变量列表
    // class:获取哪个类成员变量列表
    // count:成员变量总数
    int count = 0;

    // 成员变量数组 指向数组第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)];
        // 成员变量名称转换key
        NSString *key = [ivarName substringFromIndex:1];
        
        // 从字典中取出对应value
        id value = dict[key];
        
        // 给模型中属性赋值
        [objc setValue:value forKey:key];
        
    }
    
    return objc;
}
Table of Contents