Runtime的使用:
1.动态获取/创建类
2.动态为一个类增加属性(关联对象)或方法
3.在程序运行过程中遍历类中的成员变量, 属性和方法
4.调换两个方法的逻辑实现(Method Swizzling)-->交叉方法
如:
//oc消息发送的底层实现.
//1.导入#import <objc/message.h>.
//2.到项目Build setting中搜索objc, 关闭 enable strict checking of objc_msgSend calls
//Person类有一个属性age
objc_msgSend(person, @selector(setAge:), 28);
NSLog(@"person.age = %ld", person.age);
1.关联对象
给类添加属性:
原理: 给一个类声明属性, 其实本质就是给这个类添加关联, 并不是直接把这个值的内存空前添加到类的存储空间.
- 比如说现在系统的button类不能满足我的要求, 你需要添加一个额外的属性,
- 一般我们的解决办法都是去继承一个类, 但是就为了一个属性而去继承一个类, 太不麻烦和不划算了,
- 这个时候 runtime的关联属性就发挥有用处了
@param object#> 给谁设置关联对象
@param key#> 关联对象唯一的key, 获取时会用到.
@param value#> 关联对象
@param policy#> 关联策略
@return void
objc_setAssociatedObject(<#id object#>, <#const void *key#>, <#id value#>, <#objc_AssociationPolicy policy#>)
objc_getAssociatedObject(<#id object#>, <#const void *key#>)
**示例代码**
@interface UIButton (Mine)
@property(nonatomic, strong) NSIndexPath *indexPath;
@end
#import "UIButton+Mine.h"
#include <objc/runtime.h>
@implementation UIButton (Mine)
- (NSIndexPath *)indexPath {
//这里的_cmd是OC中的一个关键字, 只能在方法中使用, 用来表示当前方法的地址, 等价于@selector(方法名称)的返回值.
NSLog(@"_cmd = %p", _cmd);
NSLog(@"indexPath = %p", @selector(indexPath));
//
return objc_getAssociatedObject(self, _cmd);
}
- (void)setIndexPath:(NSIndexPath *)indexPath {
//这是我设置的关联对象的key值为indexpath方法地址.
objc_setAssociatedObject(self, @selector(indexPath), indexPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
在其他地方引入该UIButton类扩展的头文件
//这里使用了关联对象哦
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.indexPath = [NSIndexPath indexPathForRow:10 inSection:10];
NSLog(@"indexpath = %ld", btn.indexPath.row);
2016-12-09 17:01:03.093 Method_Swizzling01[56915:3702360] _cmd = 0x10b3ea0a4
2016-12-09 17:01:03.093 Method_Swizzling01[56915:3702360] indexPath = 0x10b3ea0a4
2016-12-09 17:01:03.095 Method_Swizzling01[56915:3702360] indexpath = 10
2.动态获取类的信息
#import <objc/runtime.h>
#import <objc/message.h>
#import "Person.h”
void getDetailClass() {
Person *person = [Person person];
NSLog(@"[Person class] = %@", [person class]);
Class personClass = [Person class];
unsigned int outCount;
//动态获取类中的成员变量
Ivar *ivarList = class_copyIvarList(personClass, &outCount);
for(int i = 0; i < outCount; i++) {
Ivar ivar = ivarList[i];
NSLog(@"成员变量的名称 = %s", ivar_getName(ivar));
}
//动态获取类中的属性
objc_property_t *propertyList = class_copyPropertyList(personClass, &outCount);
for(int i = 0; i < outCount; i++) {
objc_property_t property = propertyList[i];
NSLog(@"属性列表的名称 = %s", property_getName(property));
}
//动态获取方法列表
Method *methodList = class_copyMethodList(personClass, &outCount);
for(int i = 0; i < outCount; i++) {
Method method = methodList[i];
NSLog(@"方法列表的名称 = %@", NSStringFromSelector(method_getName(method)));
}
}
3.交叉方法Method Swizzling
+load在每个类被装载到Runtime的时候调用, 如import到当前执行文件中去的时候, 就会调用load方法.
+initialize 在每个类第一次被发送消息的时候调用。只调用一次.
dispatch_once是一次性执行代码
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method oldMethod = class_getInstanceMethod([self class], @selector(hasPrefix:));
Method newMethod = class_getInstanceMethod([self class], @selector(dzHasPrefix:));
method_exchangeImplementations(oldMethod, newMethod);
});
}
4.动态添加方法 class_addMethod(self, SEL, IMP, 参数说明)
//隐式调用方法,
[target performSelector:@selector(resolveAdd:) withObject:@"test"];
//然后,在target对象内部重写拦截调用的方法,动态添加方法。
void runAddMethod(id self, SEL _cmd, NSString *string){
NSLog(@"add C IMP ", string);
}
//重写拦截调用的方法.进行动态添加方法.
+ (BOOL)resolveInstanceMethod:(SEL)sel{
//给本类动态添加一个方法
if ([NSStringFromSelector(sel) isEqualToString:@"resolveAdd:"]) {
//这里的”v@:*”, 表示的是 v=@encoding(void), @=encoding(id), :=encoding(SEL)
//这里整体的”v@:*”就是表示方法的签名,代表有一个参数的意思.
class_addMethod(self, sel, (IMP)runAddMethod, "v@:*”);
}
return YES;
}
网友评论