公司的内网测试环境由于网络作过了限制,比较卡,因此测试连续点击button
或者cell
时可能会屡次push
控制器.如何在代码改动范围最小的范围内来解决这个问题呢?git
程序中大量按钮没有作连续响应的校验,连续点击出现了不少没必要要的问题,例如发表帖子操做,用户手快点击屡次,就会致使同一帖子发布屡次。github
#import <UIKit/UIKit.h> //默认时间间隔 #define defaultInterval 1 @interface UIButton (Swizzling) //点击间隔 @property (nonatomic, assign) NSTimeInterval timeInterval; //用于设置单个按钮不须要被hook @property (nonatomic, assign) BOOL isIgnore; @end
#import "UIButton+Swizzling.h" #import "NSObject+Swizzling.h" @implementation UIButton (Swizzling) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self methodSwizzlingWithOriginalSelector:@selector(sendAction:to:forEvent:) bySwizzledSelector:@selector(sure_SendAction:to:forEvent:)]; }); } - (NSTimeInterval)timeInterval{ return [objc_getAssociatedObject(self, _cmd) doubleValue]; } - (void)setTimeInterval:(NSTimeInterval)timeInterval{ objc_setAssociatedObject(self, @selector(timeInterval), @(timeInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } //当按钮点击事件sendAction 时将会执行sure_SendAction - (void)sure_SendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{ if (self.isIgnore) { //不须要被hook [self sure_SendAction:action to:target forEvent:event]; return; } if ([NSStringFromClass(self.class) isEqualToString:@"UIButton"]) { self.timeInterval =self.timeInterval == 0 ?defaultInterval:self.timeInterval; if (self.isIgnoreEvent){ return; }else if (self.timeInterval > 0){ [self performSelector:@selector(resetState) withObject:nil afterDelay:self.timeInterval]; } } //此处 methodA和methodB方法IMP互换了,实际上执行 sendAction;因此不会死循环 self.isIgnoreEvent = YES; [self sure_SendAction:action to:target forEvent:event]; } //runtime 动态绑定 属性 - (void)setIsIgnoreEvent:(BOOL)isIgnoreEvent{ // 注意BOOL类型 须要用OBJC_ASSOCIATION_RETAIN_NONATOMIC 不要用错,不然set方法会赋值出错 objc_setAssociatedObject(self, @selector(isIgnoreEvent), @(isIgnoreEvent), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (BOOL)isIgnoreEvent{ //_cmd == @select(isIgnore); 和set方法里一致 return [objc_getAssociatedObject(self, _cmd) boolValue]; } - (void)setIsIgnore:(BOOL)isIgnore{ // 注意BOOL类型 须要用OBJC_ASSOCIATION_RETAIN_NONATOMIC 不要用错,不然set方法会赋值出错 objc_setAssociatedObject(self, @selector(isIgnore), @(isIgnore), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (BOOL)isIgnore{ //_cmd == @select(isIgnore); 和set方法里一致 return [objc_getAssociatedObject(self, _cmd) boolValue]; } - (void)resetState{ [self setIsIgnoreEvent:NO]; } @end
使用分类+运行时
来替换Button
的点击方法,能够设置一个时间间隔
,点击事后开启一个计时器,并关闭按钮的enable
属性,计时完成后再打开enable
.至于cell
暂时没有什么好点子.网络
优势:架构
缺点:并发
通常咱们的网络请求框架都会封装
两到三层AFN
,经过大量的block进行嵌套来完成一系列的请求
工做.因此咱们能够设置一个全局id
变量,用来记录当前点击的button
和cell
,在最底层的网络请求开始时将这个按钮/cell的enable
关闭,成功后再次打开.框架
优势:ide
缺点:测试
咱们能够控制UINavigationController
中的push
方法,代码很简单,只须要判断当前的控制器和推入的控制器是不是相同的
一个class
就行了.但有一个缺点,若原本就想push
一个相同的控制器就很尴尬了.代码以下:atom
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated { //cell由于网络请求延迟而屡次push同一页面 if (![[super topViewController] isKindOfClass:[viewController class]]) { // 若是和上一个控制器同样,隔绝此操做 [super pushViewController:viewController animated:animated]; } }
连接,这位前辈的方式很巧妙,也解决了我上面的缺点
.spa
override func performSegueWithIdentifier(identifier: String, sender: AnyObject?) { if let navigationController = navigationController { guard navigationController.topViewController == self else { return } } super.performSegueWithIdentifier(identifier, sender: sender) }