本文舉例說明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");
}