runtime 的几个应用场景
1.查看成员变量
2.字典转模型
3.方法交换
-
1.查看成员变量01-修改textField展位文本颜色,给textView添加占位提醒
把textField改成红色 详情点这里

-
2.字典转模型
// 假如这是后台收到的数据
NSDictionary *json = @{
@"tagId" : @45676000,
@"age3" : @2, //这个是age3,与本地的模型字段不一样
@"weight" : @10.3,
@"name" : @"wangcai"
};
JFDog *dog = [JFDog jf_objectWithJson:json];
NSLog(@"\n--tagid:%ld\n--age:%ld\n--weight:%f\n--name:%@\n",dog.tagId,dog.age, dog.weight, dog.name);
//本地模型类
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface JFDog : NSObject
@property (assign, nonatomic) NSInteger tagId;
@property (assign, nonatomic) CGFloat weight;
@property (assign, nonatomic) NSInteger age;
@property (copy, nonatomic) NSString *name;
@end
NS_ASSUME_NONNULL_END
//写个NSObject分类,实现字典转模型
#import "NSObject+JFJson.h"
#import <objc/runtime.h>
@implementation NSObject (JFJson)
+ (instancetype)jf_objectWithJson:(NSDictionary *)json{
id obj = [[self alloc] init];
unsigned int count;
Ivar *ivars = class_copyIvarList(self, &count);
for (int i = 0; i < count; i++) {
// 取出i位置的成员变量
Ivar ivar = ivars[I];
NSMutableString *name = [NSMutableString stringWithUTF8String:ivar_getName(ivar)];
[name deleteCharactersInRange:NSMakeRange(0, 1)];//删除取出的_字符
// 设值
id value = json[name];
if ([name isEqualToString:@"age"]) {//处理本地与网络字符不匹配的问题
value = json[@"age3"];
}
[obj setValue:value forKey:name];
}
free(ivars);
return obj;
}
@end
//这只是帮助理解字典转模型库的基本实现,离真正意义上的字典转模型库差的还很远,模型嵌套了,复杂数据结果等一大堆问题需要处理。
-
3.方法交换01-扫盲
方法交换Method Swizzling
又叫黑魔法,
#import "ViewController.h"
#import "JFDog.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
JFDog *dog = [JFDog new];
[dog eat];
}
@end
///JFDog.h
#import <Foundation/Foundation.h>
@interface JFDog : NSObject
- (void)eat;
-(void)play;
+(void)play2;
@end
#import "JFDog.h"
#import <objc/runtime.h>
@implementation JFDog
- (void)eat{
NSLog(@"dog--eat");
}
-(void)play{
NSLog(@"dog--play");
}
+(void)play2{
NSLog(@"classFunc--dog--play");
}
///load 会在类或者类的分类添加到 Objective-c runtime 时调用,
///父类的 +load 方法先于子类的 +load 方法调用,
///分类的 +load 方法先于类本身的 +load 方法调用。
+(void)load{
Method method1 = class_getInstanceMethod(self.class, @selector(eat));
Method method2 = class_getInstanceMethod(self.class, @selector(play));
// Method method3 = class_getClassMethod(self, @selector(play2)); //当然你也可以交换类方法和对象方法的实现
//进行交换
method_exchangeImplementations(method1, method2);
}
@end
///这会打印dog--play,说明方法交换成功了
-
3.方法交换02-hook
//hook翻译是钩子,吧自己的方法挂载在系统方法中,
//理念就是,先拦截住系统方法,加入处理方案,然后再转到系统方法里
需求:数组添加空对象不会崩溃
//在main函数里创建一个数组调用
NSString *obj = nil;
NSMutableArray *array = [NSMutableArray array];
[array addObject:@"jack"];
[array insertObject:obj atIndex:0];
//NSMutableArray写个分类
#import "NSMutableArray+Extension.h"
#import <objc/runtime.h>
@implementation NSMutableArray (Extension)
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 类簇:NSString、NSArray、NSDictionary,真实类型是其他类型
Class cls = NSClassFromString(@"__NSArrayM");
Method method1 = class_getInstanceMethod(cls, @selector(insertObject:atIndex:));
Method method2 = class_getInstanceMethod(cls, @selector(jf_insertObject:atIndex:));
method_exchangeImplementations(method1, method2);
});
}
- (void)jf_insertObject:(id)anObject atIndex:(NSUInteger)index{
if (anObject == nil) return;//如果是空对象,就不添加,防止奔溃
//拦截之后,再调用系统的实现,由于不知道底层实现逻辑,自己实现系统方法可能会出各种意想不到的错
[self jf_insertObject:anObject atIndex:index];//这儿看似死循环,细理逻辑,方法实现已经交换过了的
}
@end
网友评论