runtime很早以前就听说过,也试着写过一些demo,但是总感觉在应用中用不上,或者说不知道怎么使用;最初接触的是叫Method Swizzling的东西,下面先讲讲Method Swizzling;
编程有一种叫做面向切片编程AOP,具体可以定义可以去wiki查看,大概就是把一些碎片的代码跟主要功能分离;有个比较好的例子(这例子比较深刻)就是点击按钮或者进入某些特定界面,将这些用户习惯发送到服务器做大数据,这些逻辑实际上与应用逻辑没关系,所以让他们占据在整个app中显得不合适,具体看看实例:
Method Swizzling原理
每个类都维护一个方法(Method)列表,Method则包含SEL和其对应IMP的信息,方法交换做的事情就是把SEL和IMP的对应关系断开,并和新的IMP生成对应关系。
交换前:Asel->AImp Bsel->BImp
交换后:Asel->BImp Bsel->AImp
实操:
引入runtime,创建button实例
#import <objc/runtime.h>
@interface UIButton (ButtonSwizzling)
@end
.m文件实现:
#import "ButtonSwizzling.h"
@implementation UIButton (ButtonSwizzling)
//应该尽可能在+load方法中实现,这样可以保证方法一定会调用且不会出现异常
+(void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
Class selfClass = [self class];
SEL originSEL = @selector(endTrackingWithTouch:withEvent:);
//获取SEL的方法
Method originMethod = class_getInstanceMethod(selfClass, originSEL);
SEL newSEL = @selector(newendTrackingWithTouch:withEvent:);
Method newMethod = class_getInstanceMethod(selfClass, newSEL);
//class_addMethod是给方法添加实现;getImplementation是获取方法的实现;getTypeEncoding获取实现的编码类型;
//先尝试給源方法添加实现,这里是为了避免源方法没有实现的情况
bool succ = class_addMethod(selfClass, newSEL, method_getImplementation(originMethod), method_getTypeEncoding(originMethod));
if (succ) {
//取代实现
//添加成功:将源方法的实现替换到交换方法的实现
class_replaceMethod(selfClass, newSEL, method_getImplementation(originMethod), method_getTypeEncoding(originMethod));
} else {
//交换两个方法的实现
//添加失败:说明源方法已经有实现,直接将两个方法的实现交换即可
method_exchangeImplementations(originMethod, newMethod);
}
});
}
- (void)newendTrackingWithTouch:(nullable UITouch *)touch withEvent:(nullable UIEvent *)event {
NSLog(@"BUTTON 方法改变啦");
//事实上这里不是死循环哦,已经被替换成sendAction:to:forEvent:
[self newendTrackingWithTouch:touch withEvent:event];
}
@end
这样你点击button能看到console输出"BUTTON 方法改变啦"!!
封装swizzling有个很出名的库,叫ASPECT!有兴趣的可以去看看,不过感觉也没差