Runtime的用法有哪几种

1, 动态添加一个类, 就像KVO一样, 系统是在程序运行的时候根据你要监听的类, 动态添加一个新类继承自该类, 然后重写原类的setter方法并在里面通知observer的.

// 创建一个类(size_t extraBytes该参数通常指定为0, 该参数是分配给类和元类对象尾部的索引ivars的字节数。)
Class clazz = objc_allocateClassPair([NSObject class], "GoodPerson", 0);

// 添加ivar
// @encode(aType) : 返回该类型的C字符串
class_addIvar(clazz, "_name", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));

class_addIvar(clazz, "_age", sizeof(NSUInteger), log2(sizeof(NSUInteger)), @encode(NSUInteger));

// 注册该类
objc_registerClassPair(clazz);

// 创建实例对象
id object = [[clazz alloc] init];

// 设置ivar
[object setValue:@"Tracy" forKey:@"name"];

Ivar ageIvar = class_getInstanceVariable(clazz, "_age");
object_setIvar(object, ageIvar, @18);

// 打印对象的类和内存地址
NSLog(@"%@", object);

// 打印对象的属性值
NSLog(@"name = %@, age = %@", [object valueForKey:@"name"], object_getIvar(object, ageIvar));

// 当类或者它的子类的实例还存在,则不能调用objc_disposeClassPair方法
object = nil;

// 销毁类
objc_disposeClassPair(clazz);
--------------------- 

2,

通过runtime获取一个类的所有属性

Person *p = [[Person alloc] init];
[p setValue:@"Kobe" forKey:@"name"];
[p setValue:@18 forKey:@"age"];
//    p.address = @"广州大学城";
p.weight = 110.0f;

// 1.打印所有ivars
unsigned int ivarCount = 0;
// 用一个字典装ivarName和value
NSMutableDictionary *ivarDict = [NSMutableDictionary dictionary];
Ivar *ivarList = class_copyIvarList([p class], &ivarCount);
for(int i = 0; i < ivarCount; i++){
    NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivarList[i])];
    id value = [p valueForKey:ivarName];

    if (value) {
        ivarDict[ivarName] = value;
    } else {
        ivarDict[ivarName] = @"值为nil";
    }
}
// 打印ivar
for (NSString *ivarName in ivarDict.allKeys) {
    NSLog(@"ivarName:%@, ivarValue:%@",ivarName, ivarDict[ivarName]);
}

// 2.打印所有properties
unsigned int propertyCount = 0;
// 用一个字典装propertyName和value
NSMutableDictionary *propertyDict = [NSMutableDictionary dictionary];
objc_property_t *propertyList = class_copyPropertyList([p class], &propertyCount);
for(int j = 0; j < propertyCount; j++){
    NSString *propertyName = [NSString stringWithUTF8String:property_getName(propertyList[j])];
    id value = [p valueForKey:propertyName];

    if (value) {
        propertyDict[propertyName] = value;
    } else {
        propertyDict[propertyName] = @"值为nil";
    }
}
// 打印property
for (NSString *propertyName in propertyDict.allKeys) {
    NSLog(@"propertyName:%@, propertyValue:%@",propertyName, propertyDict[propertyName]);
}

// 3.打印所有methods
unsigned int methodCount = 0;
// 用一个字典装methodName和arguments
NSMutableDictionary *methodDict = [NSMutableDictionary dictionary];
Method *methodList = class_copyMethodList([p class], &methodCount);
for(int k = 0; k < methodCount; k++){
    SEL methodSel = method_getName(methodList[k]);
    NSString *methodName = [NSString stringWithUTF8String:sel_getName(methodSel)];

    unsigned int argumentNums = method_getNumberOfArguments(methodList[k]);

    methodDict[methodName] = @(argumentNums - 2); // -2的原因是每个方法内部都有self 和 selector 两个参数
}
// 打印method
for (NSString *methodName in methodDict.allKeys) {
    NSLog(@"methodName:%@, argumentsCount:%@", methodName, methodDict[methodName]);
}
---------------------

3, 

动态变量控制,动态修改变量的值

-(void)changeAge{
     unsigned int count = 0;
     //动态获取XiaoMing类中的所有属性[当然包括私有] 
     Ivar *ivar = class_copyIvarList([self.xiaoMing class], &count);
     //遍历属性找到对应age字段 
     for (int i = 0; i<count; i++) {
         Ivar var = ivar[i];
         const char *varName = ivar_getName(var);
         NSString *name = [NSString stringWithUTF8String:varName];
         if ([name isEqualToString:@"_age"]) {
             //修改对应的字段值成20
             object_setIvar(self.xiaoMing, var, @"20");
             break;
         }
     }
     NSLog(@"XiaoMing's age is %@",self.xiaoMing.age);
 }

4, 

在NSObject的分类中增加方法来避免使用KVC赋值的时候出现崩溃

在有些时候我们需要通过KVC去修改某个类的私有变量,但是又不知道该属性是否存在,如果类中不存在该属性,那么通过KVC赋值就会crash,这时也可以通过运行时进行判断。同样我们在NSObject的分类中增加如下方法。

#import "NSObject+objc.h"
#import <objc/runtime.h>

@implementation NSObject (objc)

-(BOOL)hasProperty:(NSString *)property
{
    BOOL flag = NO;
    u_int count = 0;
    Ivar *ivars = class_copyIvarList([self class], &count);
    for (int i = 0; i < count; i++) {
        const char *propertyName = ivar_getName(ivars[i]);
        NSString *propertyString = [NSString stringWithUTF8String:propertyName];
        if ([propertyString isEqualToString:property]){
            flag = YES;
        }
    }
    return flag;
}

@end

5, 

利用runtime的动态交换方法实现

+(void)run
{
    NSLog(@"Person Run.....");
}

+(void)study
{
    NSLog(@"Person study.....");
}
// 获取两个类的类方法
    Method m1 = class_getClassMethod([Person class], @selector(run));
    Method m2 = class_getClassMethod([Person class], @selector(study));
    // 开始交换方法实现
    method_exchangeImplementations(m1, m2);
   [Person run];
    [Person study];

 

6,

动态添加方法

// void(*)()
// 默认方法都有两个隐式参数,
void eat(id self,SEL sel)
{
    NSLog(@"%@ %@",self,NSStringFromSelector(sel));
}

// 当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来.
// 刚好可以用来判断,未实现的方法是不是我们想要动态添加的方法
+ (BOOL)resolveInstanceMethod:(SEL)sel
{

    if (sel == @selector(eat)) {
        // 动态添加eat方法

        // 第一个参数:给哪个类添加方法
        // 第二个参数:添加方法的方法编号
        // 第三个参数:添加方法的函数实现(函数地址)
        // 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
        class_addMethod(self, @selector(eat), eat, "v@:");

    }

    return [super resolveInstanceMethod:sel];
}

 

posted on 2019-03-25 21:19  大圣ios博客  阅读(194)  评论(0编辑  收藏  举报