*看完本篇之后你将获得:
1.了解什么是runtime
2.知道可以利用runtime做到哪些事情
3.掌握用runtime开发的常用方法
*定义
1.RunTime简称运行时,就是系统在运行的时候的一些机制,其中最主要的是消息机制。
2.对于C语言,函数的调用在编译的时候会决定调用哪个函数,编译完成之后直接顺序执行,无任何二义性。
3.OC的函数调用成为消息发送。属于动态调用过程。在编译的时候并不能决定真正调用哪个函数(事实证明,在编 译阶段,OC可以调用任何函数,即使这个函数并未实现,只要申明过就不会报错。而C语言在编译阶段就会报错)。
4.只有在真正运行的时候才会根据函数的名称找 到对应的函数来调用。
*进阶
我们为什么要学习runtime
1.runtime可以遍历类和对象的属性、成员变量、方法、协议列表
2.runtime可以动态添加/修改属性,动态添加/修改/替换方法,动态添加/修改/替换协议
3.runtime可以方法拦截调用
1.遍历类和对象的属性、成员变量、方法
通过使用runtime获取类或对象的所有属性、成员变量,包括我们在"command+左击"进去看不到,然后通过
[object setValue:forKeyPath:@""];
进行属性赋值。最常见的应该是TextField的placeholder字体颜色了:
[textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
具体代码如下:
*遍历属性:
unsigned int count;
objc_property_t *properties = class_copyPropertyList([UIView class], &count);
for (int i = 0; i < count; i++) {
const char *propertiesName = property_getName(properties[i]);
NSString *str = [NSString stringWithCString:propertiesName encoding:NSUTF8StringEncoding];
NSLog(@"propertyName : %@", str);
}
*遍历成员变量:
typedef struct objc_ivar *Ivar;
unsigned int count;
Ivar *ivars = class_copyIvarList([UIView class], &count);
for (int i = 0; i < count; i++) {
const char *ivarName = ivar_getName(ivars[i]);
NSString *str = [NSString stringWithCString:ivarName encoding:NSUTF8StringEncoding];
NSLog(@"ivarName : %@", str);
}
*遍历方法:
unsigned count = 0;
// 获取所有方法
Method *methods = class_copyMethodList([UIView class], &count);
for (int i = 0; i < count; i++) {
Method method = methods[i];
// 方法类型是SEL选择器类型
SEL methodName = method_getName(method);
NSString *str = [NSString stringWithCString:sel_getName(methodName) encoding:NSUTF8StringEncoding];
int arguments = method_getNumberOfArguments(method);
NSLog(@"methodName : %@, arguments Count: %d", str, arguments);
}
*遍历协议:
__unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
for (unsigned int i; i<count; i++) {
Protocol *myProtocal = protocolList[i];
const char *protocolName = protocol_getName(myProtocal);
NSLog(@"protocol---->%@", [NSString stringWithUTF8String:protocolName]);
}
2.runtime可以动态添加/修改属性,动态添加/修改/替换方法,动态添加/修改/替换协议
*替换方法(拦截系统方法):
Method origin = class_getInstanceMethod([UIView class], @selector(touchesBegan:withEvent:));
Method custom = class_getInstanceMethod([UIView class], @selector(custom_touchesBegan:withEvent:));
method_exchangeImplementations(origin, custom);
- (void)custom_touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
*动态添加方法:(为什么动态添加方法)
使用场景:一个类方法非常多,一次性加载到内存比较耗费资源, OC都是懒加载,有些方法可能很久不会调用(只有满足特定条件才会调用),所以就要动态添加方法。
调用eat方法
Person *p = [[Person alloc] init];
[p performSelector:@selector(eat)];
// 当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来.
// 刚好可以用来判断,未实现的方法是不是我们想要动态添加的方法
.m
#import "Person.h"
@implementation Person
void eat(id self,SEL sel){
NSLog(@"---runtime---%@ %@",self,NSStringFromSelector(sel));
}
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(eat)) {
// 动态添加eat方法
// 第一个参数:给哪个类添加方法
// 第二个参数:添加方法的方法编号
// 第三个参数:添加方法的函数实现(函数地址)
// 第四个参数:函数的类型,(返回值+参数类型)
class_addMethod(self, @selector(eat), (IMP)eat, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
*给category添加属性:
.h
#import <Foundation/Foundation.h>
@interface NSObject (Property){
}
@property (nonatomic,copy) NSString *name;
@end
.m
#import "NSObject+Property.h"
@implementation NSObject (Property)
- (void)setName:(NSString *)name
{
/*
object:保存到哪个对象中
key:用什么属性保存 属性名
value:保存值
policy:策略,strong,weak
*/
objc_setAssociatedObject(self, "name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)name
{
return objc_getAssociatedObject(self, "name");
}