目前我所了解的Runtime内容大约有:动态获取类名、动态获取类的成员变量、动态获取类的属性列表、动态获取类的方法列表、动态获取类所遵循的协议列表、动态添加新的方法、类的实例方法实现的交换、动态属性关联、消息发送与消息转发机制
1、实现方法交换
<pre>
-(void)initWithVie
{
UIButton *btu = [UIButton buttonWithType:UIButtonTypeCustom];
btu.frame = CGRectMake(100, 100, 80, 40);
btu.backgroundColor = [UIColor orangeColor];
[btu addTarget:self action:@selector(clickent:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btu];
}
-(void)clickent:(UIButton *)btn
{
Method m1 = class_getInstanceMethod([self class], @selector(stringStr));
Method m2 = class_getInstanceMethod([self class], @selector(withview));
method_exchangeImplementations(m1, m2);
[self stringStr];
[self withview];
}
-(void)stringStr
{
NSLog(@"%s ---1",FUNCTION);
}
-(void)withview
{
NSLog(@"%s---2",FUNCTION);
}
</pre>
原理:OC的方法调用会在底层转换为向该对象发送消息。这由C语言的函数完成。是通过查找接收消息对象的方法列表,从方法列表中查找对应的SEL,这个SEL对应着一个IMP(一个IMP可以对应多个SEL),通过这个IMP找到对应的方法调用。
在每个类中都有一个Dispatch Table,这个Dispatch
Table本质是将类中的SEL和IMP(可以理解为函数指针)进行对应。而我们的Method
Swizzling就是对这个table进行了操作,让SEL对应另一个IMP。
如图
交换前:
003YMHs7gy72CEFH1e525&690.jpeg
交换后
003YMHs7gy72CEKEp7r51&690.jpeg2.获取类名 方法
<pre>
/**
获取类名
@param class 相应类
@return NSString:类名
*/
- (NSString *)fetchClassName:(Class)class {
const char *className = class_getName(class);
return [NSString stringWithUTF8String:className];
}
用法:
const char *className = class_getName([TestClass class]);
NSLog(@"%@",[NSString stringWithUTF8String:className]);
</pre>
3.获取成员变量
<pre>/**
获取成员变量
@param class Class
@return NSArray
*/
-
(NSArray *)fetchIvarList:(Class)class {
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList(class, &count);NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count];
for (unsigned int i = 0; i < count; i++ ) {
NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:2];
const char *ivarName = ivar_getName(ivarList[i]);
const char *ivarType = ivar_getTypeEncoding(ivarList[i]);
dic[@"type"] = [NSString stringWithUTF8String: ivarType];
dic[@"ivarName"] = [NSString stringWithUTF8String: ivarName];[mutableList addObject:dic];
}
free(ivarList);
return [NSArray arrayWithArray:mutableList];
}
常量:共享一块内存空间,就算项目中N处用到,也不会分配N块内存空间,可以根据const修饰的位置设定能否修改,在编译阶段会执行类型检查
调用方法
[Ruck fetchIvaList:[TestClass class]]unsigned int count = 0;
Ivar *ivarList = class_copyIvarList([TestClass class], &count);
NSMutableArray *mutable = [NSMutableArray arrayWithCapacity:count];
for (unsigned int i = 0; i < count; i++) {
NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:2];
const char *ivarName = ivar_getName(ivarList[i]);
const char *ivarType = ivar_getTypeEncoding(ivarList[i]);
dic[@"type"] = [NSString stringWithUTF8String: ivarType];
dic[@"ivarName"] = [NSString stringWithUTF8String: ivarName];
[mutable addObject:dic];
}
free(ivarList);
NSLog(@"is a mutable--%@",mutable);
</pre>
3.获取类的属性列表, 包括私有和公有属性,以及定义在延展中的属性
<pre>/**
获取类的属性列表, 包括私有和公有属性,以及定义在延展中的属性
@param class Class
@return 属性列表数组
*/
-
(NSArray *)fetchPropertyList:(Class)class {
unsigned int count = 0;
objc_property_t *propertyList = class_copyPropertyList(class, &count);NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count];
for (unsigned int i = 0; i < count; i++ ) {
const char *propertyName = property_getName(propertyList[i]);
[mutableList addObject:[NSString stringWithUTF8String: propertyName]];
}
free(propertyList);
return [NSArray arrayWithArray:mutableList];
}
</pre>
4.获取类的实例方法列表:getter, setter, 对象方法等。但不能获取类方法
<pre>/**
获取类的实例方法列表:getter, setter, 对象方法等。但不能获取类方法
@param class <#class description#>
@return <#return value description#>
*/
-
(NSArray *)fetchMethodList:(Class)class {
unsigned int count = 0;
Method *methodList = class_copyMethodList(class, &count);NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count];
for (unsigned int i = 0; i < count; i++ ) {
Method method = methodList[i];
SEL methodName = method_getName(method);
[mutableList addObject:NSStringFromSelector(methodName)];
}
free(methodList);
return [NSArray arrayWithArray:mutableList];
}
</pre>
- 获取协议列表
<pre>/**
获取协议列表
@param class <#class description#>
@return <#return value description#>
*/
-
(NSArray *)fetchProtocolList:(Class)class {
unsigned int count = 0;
__unsafe_unretained Protocol **protocolList = class_copyProtocolList(class, &count);NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count];
for (unsigned int i = 0; i < count; i++ ) {
Protocol *protocol = protocolList[i];
const char *protocolName = protocol_getName(protocol);
[mutableList addObject:[NSString stringWithUTF8String: protocolName]];
}return [NSArray arrayWithArray:mutableList];
return nil;
}
</pre>
- 往类上添加新的方法与其实现
<pre>
/**
往类上添加新的方法与其实现
@param class 相应的类
@param methodSel 方法的名
@param methodSelImpl 对应方法实现的方法名
*/
- (void)addMethod:(Class)class method:(SEL)methodSel method:(SEL)methodSelImpl {
Method method = class_getInstanceMethod(class, methodSelImpl);
IMP methodIMP = method_getImplementation(method);
const char *types = method_getTypeEncoding(method);
class_addMethod(class, methodSel, methodIMP, types);
}
- (void)method1 {
NSLog(@"我是Method1的实现");
}
//运行时方法拦截
- (void)dynamicAddMethod: (NSString *) value {
NSLog(@"OC替换的方法:%@", value);
}
/**
没有找到SEL的IML实现时会执行下方的方法
@param sel 当前对象调用并且找不到IML的SEL
@return 找到其他的执行方法,并返回yes
*/
- (BOOL)resolveInstanceMethod:(SEL)sel {
return NO; //当返回NO时,会接着执行forwordingTargetForSelector:方法,
[RuntimeKit addMethod:[self class] method:sel method:@selector(dynamicAddMethod:)];
return YES;
}
/**
将当前对象不存在的SEL传给其他存在该SEL的对象
@param aSelector 当前类中不存在的SEL
@return 存在该SEL的对象
*/
-
(id)forwardingTargetForSelector:(SEL)aSelector {
return self;
return [SecondClass new]; //让SecondClass中相应的SEL去执行该方法
} -
(NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
//查找父类的方法签名
NSMethodSignature *signature = [super methodSignatureForSelector:selector];
if(signature == nil) {
signature = [NSMethodSignature signatureWithObjCTypes:"@@:"];}
return signature;
} -
(void)forwardInvocation:(NSInvocation *)invocation {
SecondClass * forwardClass = [SecondClass new];
SEL sel = invocation.selector;
if ([forwardClass respondsToSelector:sel]) {
[invocation invokeWithTarget:forwardClass];
} else {
[self doesNotRecognizeSelector:sel];
}
}
</pre>
网友评论