Runtime初探

关于runtime简单来说就是运行时,是系统运行的一些机制,其中最主要的是消息机制。
example:

OC里面调用方法:
[objc setName];
在编译的时候runtime会将上述代码转换成: objc_msgSend(objc,@seletor(setName));

更详细的定义网上有许多,这里就不详细介绍,这里主要介绍几个常用的方法。


一、 数据的转换(字典到model)
class_copyPropertyList可以获取当前类的属性

///通过运行时获取当前对象的所有属性的名称,以数组的形式返回
- (NSArray *) allPropertyNames{
    ///存储所有的属性名称
    NSMutableArray *allNames = [[NSMutableArray alloc] init];
    
    ///存储属性的个数
    unsigned int propertyCount = 0;
    
    ///通过运行时获取当前类的属性
    objc_property_t *propertys = class_copyPropertyList([self class], &propertyCount);
    
    //把属性放到数组中
    for (int i = 0; i < propertyCount; i ++) {
        ///取出第一个属性
        objc_property_t property = propertys[i];
        
        const char * propertyName = property_getName(property);
        
        [allNames addObject:[NSString stringWithUTF8String:propertyName]];
    }
    
    ///释放
    free(propertys);
    
    return allNames;
}

/// 通过字符串来创建该字符串的Setter方法,并返回
- (SEL) creatSetterWithPropertyName: (NSString *) propertyName{
    
    //1.首字母大写
    propertyName = propertyName.capitalizedString;
    
    //2.拼接上set关键字
    propertyName = [NSString stringWithFormat:@"set%@:", propertyName];
    
    //3.返回set方法
    return NSSelectorFromString(propertyName);
}

/// 把字典赋值给当前实体类的属性
-(void) assginToPropertyWithDictionary: (NSDictionary *) data{
    
    if (data == nil) {
        return;
    }
    
    ///1.获取字典的key
    NSArray *dicKey = [data allKeys];
    
    ///2.循环遍历字典key, 并且动态生成实体类的setter方法,把字典的Value通过setter方法
    ///赋值给实体类的属性
    for (int i = 0; i < dicKey.count; i ++) {
        
        ///属性的存在
        if ([[self allPropertyNames]containsObject:dicKey[i]]) {
            ///2.1 通过getSetterSelWithAttibuteName 方法来获取实体类的set方法
            SEL setSel = [self creatSetterWithPropertyName:dicKey[i]];
            
            if ([self respondsToSelector:setSel]) {
                ///2.2 获取字典中key对应的value
                NSString  *value = [NSString stringWithFormat:@"%@", data[dicKey[i]]];
                
                ///2.3 把值通过setter方法赋值给实体类的属性
                [self performSelectorOnMainThread:setSel
                                       withObject:value
                                    waitUntilDone:[NSThread isMainThread]];
            }
        }
    }
}
  • 思考
    上面的代码是在model的成员变量的名称和字典的key一致的情况下,如何实现不一致的情况?(一一映射)

二、 归档
实现NSCoding协议,class_copyIvarList可以获取当前类的成员变量

- (void)encodeWithCoder:(NSCoder *)aCoder {
    Class c = self.class;
    // 截取类和父类的成员变量
    while (c && c != [NSObject class]) {
        unsigned int count = 0;
        
        Ivar *ivars = class_copyIvarList(c, &count);
        
        for (int i = 0; i < count; i++) {
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            
            id value = [self valueForKey:key];
            
            [aCoder encodeObject:value forKey:key];
            
        }
        c = [c superclass];
        
        // 释放内存
        free(ivars);
    }
}
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder{
    self = [super init];
    
    if (self) {
        Class c = self.class;
        
        // 截取类和父类的成员变量
        while (!c && c != [NSObject class]) {
            
            unsigned int count = 0;
            
            Ivar *ivars = class_copyIvarList([self class], &count);
            
            for (int i=0; i<count; i++) {
                Ivar ivar = ivars[i];
                
                NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
                
                id value = [aDecoder decodeObjectForKey:key];
                
                [self setValue:value forKey:key];
            }
            
            c = [c superclass];
            free(ivars);
        }
        
    }
    return self;
}

三、 关联对象
分类不可以创建属性,但是可以通过运行时来设置成员变量

static NSString *value = nil;

//设置关键key
static const char associatedkey;

//保存value值
    value = @"test";
    objc_setAssociatedObject(self, &associatedkey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

//通过 objc_getAssociatedObject 获取关联对象
    value = objc_getAssociatedObject(self, &associatedkey);

四、 方法替换
增加一个分类,在分类中实现:

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        
        SEL originalSelector = @selector(viewWillAppear:);
        SEL swizzledSelector = @selector(xxx_viewWillAppear:);
        
        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        
        // 如果 swizzling 的是类方法, 采用如下的方式:
        // Class class = object_getClass((id)self);
        // ...
        // Method originalMethod = class_getClassMethod(class, originalSelector);
        // Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
        
        //交换实现
        method_exchangeImplementations(originalMethod, swizzledMethod);
    });
}

///Method Swizzling
- (void)xxx_viewWillAppear:(BOOL)animated {
    [self xxx_viewWillAppear:animated];
    NSLog(@"viewWillAppear: %@", self);
}

应用场景:一个项目,替换所有图片,但是图片多了一个后缀“_new”,可以替换imageName方法;埋点;

五、 方法拦截
方法拦截并转发

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值