本文举例说明Runtime的一下几个用途:
1、拦截并替换方法
2、给分类添加属性
3、字典转模型
4、动态添加方法,处理一个未实现方法和去除报错
5、动态设置变量的值,可设置私有属性
6、实现NSCoding协议,完成归档和解档
7、获取属性、成员变量、方法(类/实例)、协议
8、添加方法、替换原方法、交换方法
9、动态添加方法
1、在分类为系统方法添加功能
例:输出UIImage imageNamed: 图片加载成功与否
#import "UIImage+image.h"
#import <objc/runtime.h>
@implementation UIImage (image)
#pragma mark - 把类加载进内存时调用,只会调用一次
+ (void)load {
// class_getClassMethod 获取某个类的方法
// method_exchangedImplementations 交换两个方法的地址
// 1、获取系统方法
Method imageNamedMethod = class_getClassMethod(self, @selector(imageNamed:));
// 2、获取自定义的方法
Method in_imageNamedMethod = class_getClassMethod(self, @selector(in_imageNamed:));
// 3、交换方法地址
method_exchangeImplementations(imageNamedMethod, in_imageNamedMethod);
}
+ (UIImage *)in_imageNamed:(NSString *)name {
// 这里调用的是系统的imageNamed,因为已经替换过了
UIImage *image = [UIImage in_imageNamed:name];
if (image) {
NSLog(@"图片加载成功");
} else {
NSLog(@"图片加载失败");
}
return image;
}
2、给系统分类添加属性
例:给NSObject添加name属性
#import "NSObject+property.h"
#import <objc/runtime.h>
@implementation NSObject (property)
- (void)setName:(NSString *)name {
// objc_setAssociatedObject 将某个值 赋值给某个对象的某个属性
objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)name {
return objc_getAssociatedObject(self, @"name");
}
3、字典转模型 (包含字典套字典,字典套数组 情况)
(1)NSObject分类实现
#import "NSObject+arrayContain.h"
#import <objc/runtime.h>
@implementation NSObject (arrayContain)
+ (instancetype)modelWithDict:(NSDictionary *)dict {
id objc = [[self alloc] init];
// 成员变量个数
unsigned int count = 0;
// 获取类中的所有成员变量
Ivar *ivarList = class_copyIvarList(self, &count);
// 遍历所有成员变量
for (int i = 0; i < count; i++) {
Ivar ivar = ivarList[i];
// 类型
NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
// @"User" -> User
ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
// 名字 ivar_getName
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
// 去掉name前面的"_"
NSString *key = [ivarName substringFromIndex:1];
// 根据名字取字典里找对应的value
id value = dict[key];
// -------- 字典里还有字典 --------
if ([value isKindOfClass:[NSDictionary class]] && ![ivarType hasPrefix:@"NS"]) {
// 根据类型名 生成类对象
Class modelClass = NSClassFromString(ivarType);
if (modelClass) { // 有对应的类型才转
value = [modelClass modelWithDict:value];
}
}
// -------- 字典里有数组 --------
if ([value isKindOfClass:[NSArray class]]) {
if ([self respondsToSelector:@selector(arrayContainModelClass)]) {
// 转换成id类型,就能调用任何对象的方法
id idSelf = self;
// 获取数组中字典对应的模型
NSString *type = [idSelf arrayContainModelClass][key];
// 生成模型
Class classModel = NSClassFromString(type);
NSMutableArray *arrM = [NSMutableArray array];
for (NSDictionary *dic in value) {
id model = [classModel modelWithDict:dic];
[arrM addObject:model];
}
value = arrM;
}
}
if (value) { // 给属性赋值
[objc setValue:value forKey:key];
}
}
return objc;
}
(2)定义一个协议,返回字典: 数组名 对应的 类名
#import <Foundation/Foundation.h>
@protocol NSObjectDelegate <NSObject>
+ (NSDictionary *)arrayContainModelClass;
@end
@interface NSObject (arrayContain)
+ (instancetype)modelWithDict:(NSDictionary *)dict;
@end
#import "NSObject+arrayContain.h"
@interface Student : NSObject <NSObjectDelegate, NSCoding>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSNumber *age;
@property (nonatomic, strong) NSArray *friends;
#import <objc/runtime.h>
@implementation Student
+ (NSDictionary *)arrayContainModelClass {
return @{@"friends":NSStringFromClass([self class])};
}
即可实现字典转模型了:
// -------- 字典转模型 --------
NSDictionary *friend = @{@"name":@"huhu", @"age":@25};
NSDictionary *dic = @{@"name":@"momo", @"age":@24, @"friends":@[friend, friend]};
Student *stu = [Student modelWithDict:dic];
NSLog(@"%@", stu.name);
4、动态添加方法处理一个未实现的方法 和 去除报错
@implementation Student
#pragma mark - 处理未实现方法
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == NSSelectorFromString(@"run:")) {
class_addMethod(self, sel, (IMP)aaa, "v@:@");
return YES;
}
return [super resolveInstanceMethod:sel];
}
// 任何方法默认都有两个隐式参数:self _cmd(当前方法的编号)
void aaa(id self, SEL _cmd, NSNumber *meter) {
NSLog(@"跑了%@米", meter);
}
[stu performSelector:@selector(run:) withObject:@10];
输出: 跑了10米
5、动态变量控制(可以操作私有)
@interface ViewController ()
@property (nonatomic, strong) Student *xiaoMing;
@end
// -------- 5、动态变量控制 --------
self.xiaoMing = [Student modelWithDict:@{@"name":@"xiaoming", @"age":@22}];
unsigned int count = 0;
Ivar *ivar = class_copyIvarList([self.xiaoMing class], &count);
for (int i = 0; i < count; i++) {
Ivar var = ivar[i];
// 成员变量 -> 属性名
const char *varName = ivar_getName(var);
NSString *name = [NSString stringWithUTF8String:varName];
// 属性名 -> 成员变量
Ivar ivar = class_getClassVariable([self.xiaoMing class], varName);
if ([name isEqualToString:@"_name"]) {
// 也可对私有变量赋值
object_setIvar(self.xiaoMing, var, @"xiaohong");
}
if ([name isEqualToString:@"_age"]) {
object_setIvar(self.xiaoMing, var, @20);
}
}
NSLog(@"xiaoMing %@", self.xiaoMing.age);
NSLog(@"xiaoMing %@", self.xiaoMing.name);
6、实现NSCoding的归档和解档
#pragma mark - 归档
- (void)encodeWithCoder:(NSCoder *)aCoder {
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivarList[i];
const char *name = ivar_getName(ivar);
NSString *key = [NSString stringWithUTF8String:name];
id value = [self valueForKey:key];
[aCoder encodeObject:value forKey:key]; // 归档
}
free(ivarList);
}
#pragma mark - 解档
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i++) {
Ivar var = ivarList[i];
const char *name = ivar_getName(var);
NSString *key = [NSString stringWithUTF8String:name];
id value = [aDecoder decodeObjectForKey:key]; // 解档
[self setValue:value forKey:key];
}
free(ivarList);
}
return self;
}
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [path stringByAppendingString:[NSString stringWithFormat:@"student:%@", stu.name]];
[NSKeyedArchiver archiveRootObject:stu toFile:filePath]; // 写
stu = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath]; // 读
7、获取 属性、成员变量、方法(类/实例)、协议
// 属性列表
objc_property_t *propertyList = class_copyPropertyList([self.xiaoMing class], &count);
for (int i = 0; i < count; i++) {
const char *name = property_getName(propertyList[i]);
NSLog(@"property : %@", [NSString stringWithUTF8String:name]);
}
Class StudentClass = object_getClass([stu class]);
// 成员变量列表
Ivar *allIvar = class_copyIvarList(StudentClass, &count);
// 方法列表
Method *methodList = class_copyMethodList([self.xiaoMing class], &count);
for (int i = 0; i < count; i++) {
Method method = methodList[i];
NSLog(@"method : %@", NSStringFromSelector(method_getName(method)));
}
// 类方法
Method *allMethod = class_copyMethodList(StudentClass, &count); // 类的所有方法
SEL oriSEL = @selector(arrayContainModelClass);
Method oriMethod = class_getClassMethod(StudentClass, oriSEL);
NSLog(@"class method : %@", NSStringFromSelector(method_getName(oriMethod)));
// 实例方法
SEL instanceSEL = @selector(thinking);
Method instanceMethod = class_getInstanceMethod([stu class], instanceSEL);
NSLog(@"instance method : %@", NSStringFromSelector(method_getName(instanceMethod)));
// 协议
__unsafe_unretained Protocol **protocolList = class_copyProtocolList([stu class], &count);
for (int i = 0; i < count; i++) {
Protocol *protocol = protocolList[i];
const char *protocolName = protocol_getName(protocol);
NSLog(@"protocol : %@", [NSString stringWithUTF8String:protocolName]);
}
8、添加方法、替换原方法、交换方法
// 添加方法
Method cusMethod;
BOOL addFunc = class_addMethod(StudentClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod));
// 替换原方法
class_replaceMethod(StudentClass, cusMethod, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
// 交换两个方法
method_exchangeImplementations(oriMethod, cusMethod);
9、动态添加方法
// -------- 动态添加方法 --------
class_addMethod([Student class], @selector(sayHi), (IMP)myAddingFunction, 0);
if ([self.xiaoMing respondsToSelector:@selector(sayHi)]) {
[self.xiaoMing performSelector:@selector(sayHi) withObject:nil];
}
}
void myAddingFunction(id self, SEL _cmd) {
NSLog(@"hi");
}