前提工作导入#import <objc/message.h>
runtime的使用场景: 1. 消息机制 ; 2. 交换方法 ; 3.动态添加方法
; 4. 动态添加属性;
任何方法的调用本质: 就是发送一个消息,用runtime发送消息,
* //类方法的本质: 类对象调用[NSObject class]
1. 消息机制 :
* 1. 不得不用消息机制, 可以帮助开发者调用私有方法;
* 2. 修改系统方法的实现的时候
//id : 谁发送消息
//SEL : 发送什么消息
//Xcod6 : 使用objc_msgSend() 有参数提示, xcod6之后没有参数提示(打开提示build settings --> 搜索msg, --> 设置为No),原因是苹果不想让开发者了解runtime的底层实现,
//最终代码需要把当前代码重新编译,用xcod, clang命令 -----> clang -rewrite-objc main.m
id objc = objc_msgSend([NSObject class], @selector(alloc)); //相当于 id objc = [NSObject alloc]
objc = objc_msgSend(objc , @selector(init)); //相当于 objc =[objc init];
创建一个Person类
#import@interface Person : NSObject
- (void)eat;
- (void)run:(NSInteger)metre;
@end
#import "Person.h"#import@implementation Person
- (void)eat{
NSLog(@"吃");
}
- (void)run:(NSInteger)metre{
NSLog(@"metre = %ld",(long)metre);
}
@end
Person * p = objc_msgSend(objc_getClass("Person"),sel_registerName("alloc")); //相当于 Person * p =[Person alloc];
p = objc_msgSend(p, sel_registerName("init")); // 相当于p = [p init];
//调用eat方法;
如果将eat方法注释掉 用OC的[p eat]会报错,只能用runtime的方法调用私有方法, runtime底层会自己查找eat方法
objc_msgSend(p, @selector(eat)); //不带参数 [p eat];
objc_msgSend(p, @selector(run:),20); //带参数的 [p run:20];
//方法的调用流程:(以eat方法为列)
//怎么调用(eat)方法: -->对象方法: 保存到类对象的方法列表(MethodList)里面 ; 类方法: 保存在元类中的方法列表(MethodList)中
// 1. 通过isa指针到对应的类中查找,
// 2. 把方法名注册为方法编号(_cmd)
// 3. 根据方法编号去查找对应的方法(而不是根据方法名去查找对应的方法)
// 4. 找到的只是最终函数实现地址,根据地址到方法区(内存)调用对应的函数(方法的实现不是保存子在类里面而是在内存的方法区)
2. runtime(交换方法):
* 需求: 每次UIImage加载图片, 告诉是否加载成功 (给系统的imageNamed添加方法)
// 给系统的imageNamed添加方法 : 1. 给系统的方法添加分类 2. 自己实现带有扩展功能的方法 3 . 交换方法, 交换一次就可以了
* 1. 自定义UIImage ( 每次使用都需要导入自定义的类, 2 .项目太大,没办法实现 )
* 2. 分类 (分类中尽最好不要重写系统的方法, 一旦重写就会把系统的方法实现干掉)
#import@interface UIImage (category)
+ (UIImage *)my_imageNamed:(NSString *)name;
@end
#import "UIImage+category.h"#import@implementation UIImage (category)
//把类加载进内存的时候调用, 只会调用一次
+ (void)load{ // swift中只能用initialize方法
//获取imageNamed方法:
Method imageNameMethod = class_getClassMethod(self, @selector(imageNamed:));
//获取my_imageNamed方法:
Method my_imageNamedMethod = class_getClassMethod(self, @selector(my_imageNamed:));
// 交换方法: runtime
method_exchangeImplementations(imageNameMethod, my_imageNamedMethod);
}
//
+ (UIImage *)my_imageNamed:(NSString *)name{
UIImage * image = [UIImage my_imageNamed:name];
if (image) {
NSLog(@"加载成功");
}else{
NSLog(@"加载失败");
}
return image;
}
- (void)viewDidLoad {
[super viewDidLoad];
UIImage * image = [UIImage imageNamed:@"1.png"];//调用imageNamed时候 ==> 调用my_imageNamed方法
}
3.动态添加方法
runtime(动态添加方法): OC是懒加载机制,只要一个方法实现了, 就会马上添加到方法列表中.
performSelector什么使用, 动态添加方法的时候使用, 为什么动态的添加方法(对收费版的APP需要用到runtime动态添加方法)
创建Car类
#import@interface Car : NSObject
@property (assign, nonatomic) NSInteger tel;
@property (strong, nonatomic) NSString * sex;
@end
#import "Car.h"#import@implementation Car
//方法 (函数)
void abc(id self, SEL _cmd) {
NSLog(@"红旗");
}
//任何方法默认都有两个隐式参数: self , _cmd
//只要一个对象调用了一个未实现的方法就会调用这个方法,进行处理
// 作用: 动态添加方法, 处理未实现的方法
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == NSSelectorFromString(@"name")) {
/**
* @param Class : 给那个类添加方法
* @param SEL : 添加那个方法
* @param IMP : 方法实现 --> 函数 --> 函数入口 --> 函数名
* @param type : 方法类型
* @return
*/
class_addMethod(self, sel, (IMP)abc, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
Car * car = [[Car alloc]init];
[car performSelector:@selector(name)];
}
4. 动态添加属性
Runtime(动态添加属性): 什么情况下需要动态添加属性(给系统的类添加属性的时候, 可以用runtime动态添加属性的方法)
* 需求:让一个NSObject 类保存一个字符串
* 动态添加属性的本质: 让某个属性与对象产生关联
* runtime一般都是针对系统的类,
#import <Foundation/Foundation.h>
@interface NSObject (propertyCategory)
//@property在分类里面的只会产生get,set方法的声明,不会生成实现,也不会生成下划线成员属性
@property NSString * name;
@end
#import "NSObject+propertyCategory.h"
#import@implementation NSObject (propertyCategory)
- (void)setName:(NSString *)name{//让这个字符串与当前对象产生联系
// object : 给哪个对象添加属性 ; key : 属性名称 ; value : 属性值 policy : 保存策略
objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}
- (NSString *)name{
return objc_getAssociatedObject(self, @"name");
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
NSObject * objc = [[NSObject alloc]init];
objc.name = @"789";//动态给NSObject添加name属性
NSLog(@"%@",objc.name);
}