博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
解决iOS因为网络卡顿点击button或者cell多次push的bug
阅读量:6887 次
发布时间:2019-06-27

本文共 3631 字,大约阅读时间需要 12 分钟。

hot3.png

 

需求

公司的内网测试环境因为网络做过了限制,比较卡,所以测试连续点击button或者cell时可能会多次push控制器.如何在代码改动范围最小的范围内来解决这个问题呢?

方法:防止按钮重复暴力点击

程序中大量按钮没有做连续响应的校验,连续点击出现了很多不必要的问题,例如发表帖子操作,用户手快点击多次,就会导致同一帖子发布多次。

#import 
//默认时间间隔#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变量,用来记录当前点击的buttoncell,在最底层的网络请求开始时将这个按钮/cell的enable关闭,成功后再次打开.

优点:

  • 能解决问题

缺点:

  • 记录cell点击,改动也不小
  • 并发的问题
  • 项目架构可能也有不适用的地方

方法三(推荐)

我们可以控制UINavigationController中的push方法,代码很简单,只需要判断当前的控制器和推入的控制器是否是相同的一个class就好了.但有一个缺点,若本来就想push一个相同的控制器就很尴尬了.代码如下:

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated    {        //cell因为网络请求延迟而多次push同一页面        if (![[super topViewController] isKindOfClass:[viewController class]]) {  // 如果和上一个控制器一样,隔绝此操作            [super pushViewController:viewController animated:animated];        }    }

方法四(强烈推荐)

,这位前辈的方式很巧妙,也解决了我上面的缺点.

override func performSegueWithIdentifier(identifier: String, sender: AnyObject?) {if let navigationController = navigationController {    guard navigationController.topViewController == self else {        return    }}super.performSegueWithIdentifier(identifier, sender: sender)}

 

转载于:https://my.oschina.net/u/2345393/blog/1608219

你可能感兴趣的文章
环境列表
查看>>
windows常用命令
查看>>
jqgrid 选中行触发编辑,切换下一行时验证和异步保存上一行数据
查看>>
Mac电脑 阿里云ECS(ContentOS) Apache+vsftpd+nodejs+mongodb建站过程总结
查看>>
编程珠玑:单词频率最高选取
查看>>
公钥、私钥、摘要、数字签名、证书
查看>>
ascii
查看>>
JavaScript from jQuery
查看>>
document.defaultView 和window
查看>>
JS配置文件动态加载CSS,js和定义请求路径
查看>>
kafka配置文件注解
查看>>
文件的拷贝
查看>>
使用MTL库求解矩阵特征值和特征向量
查看>>
UVa10006 Carmichael Numbers【素数判定+快速模幂】
查看>>
HDU1319 POJ1595 UVA406 UVALive5490 ZOJ1312 Prime Cuts【素数筛选+打表】
查看>>
【基础篇】DatePickerDialog日期控件的基本使用(二) ——分别获取年、月、日、时、分...
查看>>
理解 Android Build 系统
查看>>
hdu4632
查看>>
DotNetCore跨平台~功能测试TestHost的使用
查看>>
ASP.NET动态生成静态页面(C#)
查看>>