首先导入头文件
#import <objc/runtime.h>
markdown
经过runtime的一系列方法,能够获取类的一些信息, 包括:属性列表,方法列表,成员变量列表,和遵循的协议列表。函数
有时候会有这样的需求,咱们须要知道当前类中每一个属性的名字。spa
unsigned int count;
// 获取列表
objc_property_t *propertyList = class_copyPropertyList([self class], &count);
for (unsigned int i=0; i<count; i++) {
// 获取属性名
const char *propertyName = property_getName(propertyList[i]);
// 打印
NSLog(@"property-->%@", [NSString stringWithUTF8String:propertyName]);
}
复制代码
Method *methodList = class_copyMethodList([self class], &count);
for (unsigned int i; i<count; i++) {
Method method = methodList[i];
NSLog(@"method-->%@", NSStringFromSelector(method_getName(method)));
}
复制代码
Ivar *ivarList = class_copyIvarList([self class], &count);
for (unsigned int i; i<count; i++) {
Ivar myIvar = ivarList[i];
const char *ivarName = ivar_getName(myIvar);
NSLog(@"Ivar---->%@", [NSString stringWithUTF8String:ivarName]);
}
复制代码
__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]);
}
复制代码
核心方法:class_addMethod ()
首先从外部隐式调用一个不存在
的方法:指针
// 隐式调用方法
[target performSelector:@selector(resolveAdd:) withObject:@"test"];
复制代码
而后,在target对象内部重写拦截调用的方法,动态添加方法。code
// 重写了拦截调用的方法,并返回YES
+ (BOOL)resolveInstanceMethod:(SEL)sel{
//给本类动态添加一个方法
if ([NSStringFromSelector(sel) isEqualToString:@"resolveAdd:"]) {
class_addMethod(self, sel, (IMP)runAddMethod, "v@:*");
}
return YES;
}
// 调用新增的方法
void runAddMethod(id self, SEL _cmd, NSString *string){
NSLog(@"add C IMP ", string); //withObject 参数
}
复制代码
其中
class_addMethod
的四个参数分别是:orm
Class
: cls 给哪一个类添加方法,本例中是self SEL name
: 添加的方法,本例中是重写的拦截调用传进来的selector。 IMP imp
: 方法的实现,C方法的方法实现能够直接得到。若是是OC方法,能够用+ (IMP)instanceMethodForSelector:(SEL)aSelector;
得到方法的实现。 "v@:*"
:方法的签名,表明有一个参数的方法。对象
如今你准备用一个系统的类,可是系统的类并不能知足你的需求,你须要额外添加一个属性。通常解决办法就是继承。 可是,只增长一个属性,就去继承一个类,以为太麻烦类。 这个时候,runtime的关联属性就发挥它的做用了。在Category中添加属性。继承
//首先定义一个全局变量,用它的地址做为关联对象的key
static char associatedObjectKey;
//设置关联对象
objc_setAssociatedObject(target, &associatedObjectKey, @"添加的字符串属性", OBJC_ASSOCIATION_RETAIN_NONATOMIC); //获取关联对象
NSString *string = objc_getAssociatedObject(target, &associatedObjectKey);
NSLog(@"AssociatedObject = %@", string);
复制代码
objc_setAssociatedObject
的四个参数:ci
id
object给谁设置关联对象。 const void *key
关联对象惟一的key,获取时会用到。 id value
关联对象。 objc_AssociationPolicy
关联策略字符串
在没有一个类的实现源码的状况下,想改变其中一个方法的实现,除了继承它重写
、和借助Category重名方法
以外,还有更加灵活的方法 Method Swizzle
。 在OC中调用一个方法,实际上是向一个对象发送消息,查找消息的惟一依据是selector的名字。 利用OC的动态特性,能够实如今运行时偷换selector对应的方法实现。
Method Swizzle
指的是,改变一个已存在的选择器对应的实现过程。OC中方法的调用可以在运行时,改变类的调度表中选择器到最终函数间的映射关系。
每一个类都有一个方法列表,存放着selector的名字及其方法实现的映射关系。IMP有点相似函数指针,指向具体的方法实现。 利用 method_exchangeImplementations
来交换2个方法中的IMP。 利用 class_replaceMethod
来修改类。 利用 method_setImplementation
来直接设置某个方法的IMP。
归根结底,都是偷换了selector
的IMP
新建分类
#import <objc/runtime.h>
+ (void)load {
// 方法交换应该被保证,在程序中只会执行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 得到须要替换的系统方法
SEL systemSel = @selector(didDisplay);
// 本身实现的将要被交换的方法
SEL swizzSel = @selector(myDidDisplay);
//两个方法的Method
Method systemMethod = class_getInstanceMethod([self class], systemSel);
Method swizzMethod = class_getInstanceMethod([self class], swizzSel);
//首先动态添加方法,实现是被交换的方法,返回值表示添加成功仍是失败
BOOL isAdd = class_addMethod(self, systemSel, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));
if (isAdd) {
//若是成功,说明类中不存在这个方法的实现
//将被交换方法的实现替换到这个并不存在的实现
class_replaceMethod(self, swizzSel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
} else {
//不然,交换两个方法的实现
method_exchangeImplementations(systemMethod, swizzMethod);
}
});
}
-(void)myDidDisplay{
//......do
}
复制代码