Runtime的实际应用2

89 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第13天,点击查看活动详情

获取所有的私有属性和方法

这个在判断是否子类重写了父类的方法时会用到。

#pragma mark - 获取所有的属性(包括私有的)
- (void)getAllIvar {
    unsigned int count = 0; 
    Ivar *ivars= class_copyIvarList([UIPageControl class], &count);
    for (int i = 0; i < count; i++) {
        Ivar ivar = ivars[i];
        NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];
        NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
        NSLog(@"属性 --> %@ 和 %@",name,type);

    }

}
#pragma mark - 获取所有的方法(包括私有的)
- (void)getAllMethod {
    unsigned int count = 0;
    Method *memberFuncs = class_copyMethodList([UIPageControl class], &count);
    for (int i = 0; i < count; i++) {

        SEL address = method_getName(memberFuncs[i]);
        NSString *methodName = [NSString stringWithCString:sel_getName(address) encoding:NSUTF8StringEncoding];

        NSLog(@"方法 : %@",methodName);
    }

}

对私有属性修改

有时候一般的api不能满足我们的需求,比如有的api的某些方法没有暴露出来或者部分字段没有公开,这时候我们又需要访问这些私有变量,那么这时候runtime就派上了用场。

下面是一个具体的对私有变量更改的例子。

- (void)changePrivate {
    Person *onePerson = [[Person alloc] init];
    NSLog(@"Person属性 == %@",[onePerson description]);

    unsigned  int count = 0;
    Ivar *members = class_copyIvarList([Person class], &count);

    for (int i = 0; i < count; i++){
        Ivar var = members[i];
        const char *memberAddress = ivar_getName(var);
        const char *memberType = ivar_getTypeEncoding(var);
        NSLog(@"获取所有属性 = %s ; type = %s",memberAddress,memberType);
    }
    Ivar m_address = members[1];
    object_setIvar(onePerson, m_address, @"上海");
    NSLog(@"对私有变量的(地址)进行更改 : %@",[onePerson description]);

}

归档:解档

快速定义归档和解档属性

@implementation MMModel

- (void)encodeWithCoder:(NSCoder *)encoder {
    unsigned int count = 0;
    //  利用runtime获取实例变量的列表
    Ivar *ivars = class_copyIvarList([self class], &count);
    for (int i = 0; i < count; i ++) { 
        Ivar ivar = ivars[i];  
        const char *name = ivar_getName(ivar);
        NSString *nameStr = [NSString stringWithCString:name encoding:NSUTF8StringEncoding]; 
        id value = [self valueForKey:nameStr];   
        [encoder encodeObject:value forKey:nameStr];
    }
    free(ivars);

}

- (id)initWithCoder:(NSCoder *)decoder {
    if (self = [super init]) {
        unsigned int count = 0;
        Ivar *ivars = class_copyIvarList([self class], &count);
        for (int i = 0; i < count; i ++) {
            Ivar ivar = ivars[i];
            const char *name = ivar_getName(ivar);

            //
            NSString *key = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
            id value = [decoder decodeObjectForKey:key];
            //  设置到成员变量身上
            [self setValue:value forKey:key];
        }

        free(ivars);
    }
    return self;
}

7、动态的添加方法

有的时候我们的代码不是写死的,有些高级场景,比如插件啊等什么的需要我们动态生成方法动态添加,这时候我们就可以利用runtime来达到这个效果。

理解了消息转发的整个流程,就能够理解为什么这样行得通。

// 默认方法都有两个隐式参数,
void eat(id self,SEL sel){
    NSLog(@"%@ %@",self,NSStringFromSelector(sel));
    NSLog(@"动态添加了一个方法");

}

+ (BOOL)resolveInstanceMethod:(SEL)sel {

    if (sel == NSSelectorFromString(@"eat")) {  
        class_addMethod(self, sel, (IMP)eat, "v@:");
        return YES;
    }  
    return [super resolveInstanceMethod:sel];
}