runtime简单应用

1.获取对象属性和变量列表

这里准备了一个Model类

#import 

@interface UserModel : NSObject
{
    NSString *userPhone;
}

@property (nonatomic, copy)NSString *userName;

@property (nonatomic, assign)BOOL userGender;

@property (nonatomic, assign)NSInteger userAge;

@end

在要查看它属性列表的类里引入头文件:

#import "UserModel.h"
#import 

然后实现如下代码:

//getIvarsList
unsigned int ivarCount;//无符号int,储存范围比普通int大一倍
Ivar *ivars = class_copyIvarList([UserModel class], &ivarCount);
NSMutableArray *propertiesArray = [NSMutableArray arrayWithCapacity:ivarCount];
for (unsigned int i = 0; i < ivarCount; i++) {
    Ivar ivar = ivars[i];
    const char *type = ivar_getTypeEncoding(ivar);
    const char *name = ivar_getName(ivar);
    [propertiesArray addObject:[NSString stringWithUTF8String:name]];
    NSLog(@"成员变量的类型 %s,名称 %s ",type, name);
}
free(ivars);//class_copyIvarList是C语言的实现,必须释放
NSLog(@"%@",propertiesArray);

可以用class_copyPropertyList替换class_copyIvarList,class_copyPropertyList只能获取类的属性,class_copyIvarList可以获取属性和成员变量

打印:

2018-04-03 11:29:36.989517+0800 PodsTest[1596:98852] 成员变量的类型 @"NSString",名称 userPhone 
2018-04-03 11:29:36.989642+0800 PodsTest[1596:98852] 成员变量的类型 B,名称 _userGender 
2018-04-03 11:29:36.989737+0800 PodsTest[1596:98852] 成员变量的类型 @"NSString",名称 _userName 
2018-04-03 11:29:36.989817+0800 PodsTest[1596:98852] 成员变量的类型 q,名称 _userAge 
2018-04-03 11:29:36.989963+0800 PodsTest[1596:98852] (
    userPhone,
    "_userGender",
    "_userName",
    "_userAge"
)

2.给分类添加属性

给刚才的UserModel类添加了一个分类:

#import "UserModel.h"

@interface UserModel (Property)

@property (nonatomic, copy)NSString *userPhone;

@end

导入#import "UserModel+Property.h"头文件运行一下:

UserModel *userModel = [[UserModel alloc]init];
userModel.userPhone = @"123";
NSLog(@"%@",userModel.userPhone);

崩溃了,报错如下:

2018-04-03 12:10:48.858169+0800 PodsTest[2273:172391] -[UserModel setUserPhone:]: unrecognized selector sent to instance 0x6000002207e0
2018-04-03 12:10:48.864333+0800 PodsTest[2273:172391] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UserModel setUserPhone:]: unrecognized selector sent to instance 0x6000002207e0'

没有setUserPhone方法的实现

在分类实现文件里加入set/get方法的实现:

#import "UserModel+Property.h"
#import 

@implementation UserModel (Property)

- (void)setUserPhone:(NSString *)userPhone {
    objc_setAssociatedObject(self, @"userPhone", userPhone, OBJC_ASSOCIATION_RETAIN);
}

- (NSString *)userPhone {
    return objc_getAssociatedObject(self, @"userPhone");
}

@end

再运行:

2018-04-03 12:14:10.598727+0800 PodsTest[2358:178611] 123

成功打印

3.获取对象方法列表

给UserModel类添加几个方法:

#import "UserModel.h"

@implementation UserModel

- (void)run {
    NSLog(@"%s",__FUNCTION__);
}

- (void)eat {
    NSLog(@"%s",__FUNCTION__);
}

- (void)work {
    NSLog(@"%s",__FUNCTION__);
}

@end

实现如下代码:

//getMethodsList
unsigned int methodCount;
Method *methods = class_copyMethodList([UserModel class], &methodCount);
NSMutableArray *methodsArray = [NSMutableArray arrayWithCapacity:methodCount];
for (unsigned int i = 0; i < methodCount; i++) {
    Method method = methods[i];
    IMP imp = method_getImplementation(method);//方法调用地址
    SEL name_f = method_getName(method);//方法
    const char *name_s = sel_getName(method_getName(method));//方法名
    int arguments = method_getNumberOfArguments(method);//参数个数
    const char *encoding = method_getTypeEncoding(method);//编码方式

    [methodsArray addObject:[NSString stringWithUTF8String:name_s]];
}
free(methods);
NSLog(@"%@",methodsArray);

打印结果:

2018-04-03 12:52:05.447052+0800 PodsTest[3250:245253] (
    eat,
    work,
    run
)

如果类里有属性或成员变量,这里也会打印出set/get方法

4.访问和修改私有变量

在UserModel类里准备了私有属性和变量:

#import "UserModel.h"

@interface UserModel()

@property (nonatomic ,copy)NSString *firstName;

@end

@implementation UserModel
{
    NSString *lastName;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        self.firstName = @"Nee";
        lastName = @"Datou";
    }
    return self;
}

访问:

UserModel *userModel = [[UserModel alloc]init];
Ivar ivar1 = class_getInstanceVariable([UserModel class], "_firstName");
Ivar ivar2 = class_getInstanceVariable([UserModel class], "lastName");
NSString *ivarValue1 = object_getIvar(userModel, ivar1);
NSString *ivarValue2 = object_getIvar(userModel, ivar2);
NSLog(@"%@  %@",ivarValue1,ivarValue2);

修改:

object_setIvar(userModel, ivar1, @"first");
object_setIvar(userModel, ivar2, @"last");
NSString *ivarValueNew1 = object_getIvar(userModel, ivar1);
NSString *ivarValueNew2 = object_getIvar(userModel, ivar2);
NSLog(@"%@  %@",ivarValueNew1,ivarValueNew2);

打印结果:

2018-04-03 13:24:17.133980+0800 PodsTest[4096:306556] Nee  Datou
2018-04-03 13:24:17.134103+0800 PodsTest[4096:306556] first  last

5.动态归档/解档

当属性比较多的时候可以用runtime来遍历属性列表完成归档和解档的协议方法:

#import "UserModel.h"
#import 

@implementation UserModel

//归档协议方法
- (void)encodeWithCoder:(NSCoder *)aCoder {
//    [aCoder encodeObject:self.userName forKey:@"name"];
//    [aCoder encodeBool:self.userGender forKey:@"gender"];
//    [aCoder encodeInteger:self.userAge forKey:@"age"];
    
    unsigned int ivarCount;
    Ivar *ivars = class_copyIvarList([self class], &ivarCount);
    for (unsigned i = 0; i < ivarCount; i++) {
        Ivar ivar = ivars[i];
        NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
        [aCoder encodeObject:[self valueForKey:key] forKey:key];
    }
    free(ivars);
}

//解档协议方法
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
//    if (self = [super init]) {
//        self.userName = [aDecoder decodeObjectForKey:@"name"];
//        self.userGender = [aDecoder decodeBoolForKey:@"gender"];
//        self.userAge = [aDecoder decodeIntegerForKey:@"age"];
//    }
//    return self;
    
    if (self = [super init]) {
        unsigned int ivarCount;
        Ivar *ivars = class_copyIvarList([self class], &ivarCount);
        for (unsigned i = 0; i < ivarCount; i++) {
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            [self setValue:[aDecoder decodeObjectForKey:key] forKey:key];
        }
        free(ivars);
    }
    return self;
}

@end

6.字典转模型

在NSObject分类里添加一个方法:

#import 

@interface NSObject (Model)

+ (instancetype)initModelWithDict:(NSDictionary *)dict;

@end

利用runtime遍历属性列表实现字典到模型的转换:

#import "NSObject+Model.h"
#import 

@implementation NSObject (Model)

+ (instancetype)initModelWithDict:(NSDictionary *)dict {
    id obj = [[self alloc]init];
    
    unsigned int count;
    //获取属性列表
    objc_property_t *propertys = class_copyPropertyList([self class], &count);
    for (unsigned i = 0; i < count; i++) {
        objc_property_t property = propertys[i];
        //获取属性Key
        NSString *propertyKey = [NSString stringWithUTF8String:property_getName(property)];
        //从字典中获取属性Value
        id propertyValue = dict[propertyKey];
        if (propertyValue) {
            [obj setValue:propertyValue forKey:propertyKey];
        }
    }
    free(propertys);
    return obj;
}

@end

调用:

NSDictionary *dic = @{@"userName":@"datou",@"userGender":@"1",@"userAge":@"16"};
UserModel *userModel = [UserModel initModelWithDict:dic];
NSLog(@"%@ %@ %@",userModel.userName,userModel.userGender,userModel.userAge);

打印结果:

2018-04-03 17:11:57.115482+0800 PodsTest[6977:525390] datou 1 16

这里只是一个简单的例子,对于更复杂的json,比如字典嵌套字典,数组嵌套字典,需要二级转换、三级转换

7.方法交换

我在UserModel类里添加了两个方法:

- (void)run {
    NSLog(@"run!");
}

- (void)eat {
    NSLog(@"eat!");
}

交换它们的方法实现:

//获取run的方法实现
Method method1 = class_getInstanceMethod([UserModel class], @selector(run));
//获取eat的方法实现
Method method2 = class_getInstanceMethod([UserModel class], @selector(eat));
//交换
method_exchangeImplementations(method1, method2);
    
UserModel *userModel = [[UserModel alloc]init];
[userModel performSelector:@selector(run)];
[userModel performSelector:@selector(eat)];

打印结果:

2018-04-03 17:42:15.590656+0800 PodsTest[7465:572622] eat!
2018-04-03 17:42:15.590785+0800 PodsTest[7465:572622] run!

8.动态添加方法

UserModel *userModel = [[UserModel alloc]init];
[userModel performSelector:@selector(work)];

当一个对象调用了一个未实现的方法就会进入下面这个类方法:

+ (BOOL)resolveInstanceMethod:(SEL)sel:

可以在这个类方法里动态添加方法实现:

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(work)) {
        // 第一个参数:给哪个类添加方法
        // 第二个参数:添加方法的方法编号
        // 第三个参数:添加方法的函数实现(函数地址)
        // 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
        class_addMethod([UserModel class], @selector(work), (IMP)workFunction, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

void workFunction(id self, SEL _cmd) {
    NSLog(@"work!");
}

你可能感兴趣的:(runtime简单应用)