runtime的资料网上有很多了,部分有些晦涩难懂,我通过自己的学习方法总结一遍,主要讲一些常用的方法功能,以实用为主,我觉得用到印象才是最深刻的.
案例1:方法简单的交换
需要用到的头文件
<objc/runtime.h>
- 获得某个类的类方法
Method class_getClassMethod(Class cls , SEL name)
- 获得某个类的实例对象方法
Method class_getInstanceMethod(Class cls , SEL name)
- 交换两个方法的实现
void method_exchangeImplementations(Method m1 , Method m2)
示例
- 创建一个Person类
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
+ (void)run;
+ (void)study;
@end
NS_ASSUME_NONNULL_END
#import "Person.h"
@implementation Person
+ (void)run {
NSLog(@"跑");
}
+ (void)study {
NSLog(@"学习");
}
@end
// 调用
[Person run];
[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];
// 结果: 学习 跑
案例2:拦截系统方法
- 创建一个UIImage分类
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIImage (Extension)
@end
NS_ASSUME_NONNULL_END
#import "UIImage+Extension.h"
#import <objc/runtime.h>
@implementation UIImage (Extension)
+ (void)load {
// 获取两个类的类方法
Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
Method m2 = class_getClassMethod([UIImage class], @selector(LZY_imageNamed:));
// 开始交换方法实现
method_exchangeImplementations(m1, m2);
}
+ (UIImage *)LZY_imageNamed:(NSString *)name {
double version = [[UIDevice currentDevice].systemVersion doubleValue];
if (version >= 7.0) {
// 如果系统版本是7.0以上,使用另外一套文件名结尾是‘_os7’的扁平化图片
name = [name stringByAppendingString:@"_os7"];
}
return [UIImage LZY_imageNamed:name];
}
@end
// 调用
UIImageView *imageView = [[UIImageView alloc] init];
imageView.image = [UIImage imageNamed:@"头像"];
imageView.frame = CGRectMake(100, 100, 200, 200);
[self.view addSubview:imageView];
未替换方法之前.png
替换方法之后.png
案例3:在分类中设置属性,给任何一个对象设置属性
- set方法,将值value 跟对象object 关联起来(将值value 存储到对象object 中)
参数 object:给哪个对象设置属性
参数 key:一个属性对应一个Key,将来可以通过key取出这个存储的值,key 可以是任何类型:double、int 等,建议用char 可以节省字节
参数 value:给属性设置的值
参数policy:存储策略 (assign 、copy 、 retain就是strong)
void objc_setAssociatedObject(id object , const void *key ,id value ,objc_AssociationPolicy policy)
- 利用参数key 将对象object中存储的对应值取出来
id objc_getAssociatedObject(id object , const void *key)
- 创建一个Person分类
#import "Person.h"
NS_ASSUME_NONNULL_BEGIN
@interface Person (Extension)
/** 昵称 */
@property (nonatomic, copy) NSString *name;
@end
NS_ASSUME_NONNULL_END
#import "Person+Extension.h"
#import <objc/runtime.h>
@implementation Person (Extension)
char nameKey;
- (void)setName:(NSString *)name {
// 将某个值跟某个对象关联起来,将某个值存储到某个对象中
objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
return objc_getAssociatedObject(self, &nameKey);
}
@end
// 调用
Person *p = [[Person alloc] init];
p.name = @"小王龙王";
NSLog(@"昵称: %@", p.name);
// 结果: 小王龙王
案例4: 在分类中添加方法
/**
定义方法
@param self 本身对象
@param sel 定义的方法
@param param 参数
*/
void eat(id self, SEL sel, NSString *param) {
NSLog(@"调用eat 参数1:%@ 参数2:%@ 参数3:%@", self, NSStringFromSelector(sel), param);
}
/**
定义方法
@param self 本身对象
@param sel 定义的方法
*/
void run(id self, SEL sel) {
NSLog(@"调用run 参数1:%@ 参数2:%@", self, NSStringFromSelector(sel));
}
/**
添加对象方法
@param sel 定义的方法
@return 返回是否添加成功
*/
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(eat:)) {
/**
动态添加方法
self 为哪个类添加方法
sel 添加方法的方法编号(方法名)是什么
IMP 方法实现
返回是否添加成功
最后一个参数为"v@:@“, v:返回void, i:int ,f:double, c:unsigned char等等
*/
BOOL isSuccess = class_addMethod(self, sel, (IMP)eat, "v@:@");
return isSuccess;
}else if (sel == @selector(run)) {
BOOL isSuccess = class_addMethod(self, sel, (IMP)run, "v@:");
return isSuccess;
}
return [super resolveInstanceMethod:sel];
}
// 调用
// 带参数
Person *p = [[Person alloc] init];
[p performSelector:@selector(eat:) withObject:@"吃牛排"];
// 结果: 用eat 参数1:<Person> 参数2:eat: 参数3:吃牛排
// 无参
[p performSelector:@selector(run)];
// 结果: 用run 参数1:<Person> 参数2:run
案例5:获得一个类的所有成员变量
- 获得某个类的所有成员变量(outCount 会返回成员变量的总数)
参数:
1、哪个类
2、放一个接收值的地址,用来存放属性的个数
3、返回值:存放所有获取到的属性,通过下面两个方法可以调出名字和类型
Ivar *class_copyIvarList(Class cls , unsigned int *outCount)
- 获得成员变量的名字
const char *ivar_getName(Ivar v)
- 获得成员变量的类型
const char *ivar_getTypeEndcoding(Ivar v)
获取Person类中所有成员变量的名字和类型
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList([Person class], &outCount);
// 遍历所有成员变量
for (int i = 0; i < outCount; i++) {
// 取出i位置对应的成员变量
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
const char *type = ivar_getTypeEncoding(ivar);
NSLog(@"成员变量名:%s 成员变量类型:%s",name,type);
}
// 注意释放内存!
free(ivars);
6. 根据类对象获取类名
NSString *name = [NSString stringWithUTF8String:object_getClassName(类对象)];
// 结果: 类对象的类名
7. 获取类的父类
导入
<objc/runtime.h>头文件
#import "BaseVC.h"
@interface NotificationVC : BaseVC
@end
// 测试
Class class = class_getSuperclass([self class]);
NSLog(@"测试: %@ ", class);
// 结果: 测试: BaseVC
8.获取实例大小
size_t result = class_getInstanceSize([self class]);
NSLog(@"测试: %zu ", result);
// 结果: 测试: 1024










网友评论