记录使用runtime修改UIScrollview的经历
今天突然发现一个问题,UITableView中添加的几个按钮,点击后push到其他ViewController中,结果按钮的选中效果不显示,查了一下UIScrollView的API,发现了delaysContentTouches这个属性
@property (nonatomic) BOOL delaysContentTouches;// default is YES. if NO, we immediately call -touchesShouldBegin:withEvent:inContentView:. this has no effect on presses
这个属性默认为YES,也就是会延迟发送touchesShouldbegin这个消息,也就导致了按钮的选中效果没有及时显示出来
那么如何设置一下delaysContentTouches这个属性为NO就好了
_scrollView.delaysContentTouches=NO;
bingo,成功!!!
那么问题来了,项目中有很多的TableView、ScrollView、WebView,挨个的去设置ScrollView的这个属性,未免改动太大,而且风险较高,此时我想到了runtime,那么如何用runtime实现这个效果呢,该写到那个方法中呢?
大家都知道+(void)load和+(void)initialize方法的吧,通俗解释就是load和initialize都是在类加载进内存的时候就会执行,也就是说在main函数调用之前执行。
题外话:load和initialize两个方法都不在main函数的autoreleasepool中,是在main函数之前加载到内存中的,所以,对于这两个方法内初始化的私有变量需要程序员们来处理,怎么处理呢?ARC的话只需要@autoreleasepool封装就好了
我们选择在load中实现,首先因为initialize是在该类或者类对象被调用的时候才会执行,其次,initialize只会调用一次,而且我们要用category实现,类的每一个category类中都会执行各自的load类方法;再者,load我们要对默认值进行修改,应当尽量提前的替换掉方法块才可以
废话不多说,上代码
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originInitSel = @selector(initWithFrame:);
SEL newInitSel = @selector(xxxx_initWithFrame:);
Method originalMethod = class_getInstanceMethod(class, originInitSel);
Method swizzledMethod = class_getInstanceMethod(class, newInitSel);
BOOL didAddMethod =class_addMethod(class,originInitSel,method_getImplementation(swizzledMethod),method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,newInitSel,method_getImplementation(originalMethod),method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
/**
重置系统的默认值
让scrollview中的按钮可以随时显示按钮的按下动作
**/
- (id)xxxx_initWithFrame:(CGRect)frame{
UIScrollView* vscroll = [self xxxx_initWithFrame:frame];///一定要注意这里
vscroll.delaysContentTouches = NO;
return vscroll;
}
@end
盗用其他同学的图片:
我在这里也描述一下我个人的理解:
1.在编译阶段,llvm会生成一个方法名(字符串、SEL)、类(字符串、class name)和函数指针(IMP)之间的映射表(hashtable)
2.消息传递的概要过程:
通过方法名(SEL)查找对应的函数指针(IMP)
3.调用方法块(IMP)
那么Swizzle其实就是变更了方法名(SEL)和函数指针(IMP)之间的映射关系。
其实理解起来很简单~不明白的小伙伴可以在评论区交流一下,欢迎拍砖
UIView/UIImageView也可以用类似的方法修改任意的默认值咯
疑问:在我们的iOS开发过程中,如果一个类方法非常多,加载类到内存的时候也比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法解决。做到优化内存,节省资源的效果,这是为什么呢?
我的qq是:58901119 多交流哟~