GitHub上的Demo,是最新的代码
ClickCountOfButton
在开发的时候需要一个问题,在产品已经完成后,产品经理来了一句
今天上架,顺道统计整个App 每个按钮的点击次数,进行数据分析
东西都做完了,还整这个,项目原因不能使用友盟
,但是作为一个技术宅,如果做不出来,那不是也太逊了吗,于是对需求进行了分析
1.统计每个按钮的点击次数,可以给按钮增加一个 clickedCount
属性每次执行方法clickedCount++
2.如果对象销毁,成员属性就释放,显然这个方法的背后还要做点什么
3.如此同时,整个项目都做完,项目所有的按钮也完毕,如果要增加属性,然后在每个按钮的点击后执行的方法中要添加一些代码,这不是要搞死人的节奏吗,一个项目中的按钮, 何其多啊,而且当时使用的是UIButton
系统自带的想一想 ,我所有的点击事件,都是UIButton
,怎么拦截UIButton
的添加点击事件了,同时还要记录点击的次数
方法一 继承
UIButton
(果断放弃,因为整个项目都完成了,可以说项目在没有这个需求下是可以没有问题的,如果使用这个方法,改动太大,那么程序出错的风险也就大,同时时间太长,再次,万一哪个按钮忘记改了,那就没统计到)
方法二 分类
(不需要改源代码的任何代码,只需要增加一个分类,侵入性小,采取)
方法三 runtime
这里也用到了
使用分类重写UIButton的方法
/** 拦截了UIButton 所有的
- (void)addTarget:(nullable id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents;
方法*/
- (void)addTarget:(nullable id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents{
//block循环引用
__weak typeof(target) weakTarget = target;
//利用 关联对象 给UIButton 增加了一个 block
[self setCurrentActionBlock:^{
//运行时 发送 消息 执行方法
((void (*)(void *, SEL, UIView *))objc_msgSend)((__bridge void *)(weakTarget), action , nil);
}];
//拦截了本身要执行的action 先执行,写下来的 xw_clicked:方法
[super addTarget:self action:@selector(xw_clicked:) forControlEvents:controlEvents];
}
//拦截了按钮点击后要执行的代码
- (void)xw_clicked:(UIButton *)sender{
//统计
self.btnClickedCount++;
NSLog(@"%@ 点击 %ld次 ",[sender titleForState:UIControlStateNormal], self.btnClickedCount);
//执行原来要执行的方法
sender.currentActionBlock();
}
//在分类中增加了 btnClickedCount的 (setter 和 getter)方法,使用关联对象增加了相关的成员空间
- (NSInteger)btnClickedCount{
id tmp = objc_getAssociatedObject(self, &xw_btnClickedCount);
NSNumber *number = tmp;
return number.integerValue;
}
- (void)setBtnClickedCount:(NSInteger)btnClickedCount{
objc_setAssociatedObject(self, &xw_btnClickedCount, @(btnClickedCount), OBJC_ASSOCIATION_ASSIGN);
}
测试代码
- (void)viewDidLoad {
[super viewDidLoad];
//添加两个按钮到View上面
UIButton *btn = [UIButton buttonWithType:UIButtonTypeContactAdd];
btn.frame = (CGRect){100,200,50,50};
[btn addTarget:self action:@selector(btn1Clicked:) forControlEvents:UIControlEventTouchUpInside];
[btn setTitle:@"添加A" forState:UIControlStateNormal]; [self.view addSubview:btn];
UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeContactAdd];
btn2.frame = (CGRect){200,200,50,50};
[btn2 addTarget:self action:@selector(btn2Clicked:) forControlEvents:UIControlEventTouchUpInside];
[btn2 setTitle:@"添加B" forState:UIControlStateNormal];
[self.view addSubview:btn2];
}
#pragma mark - 按钮点击事件
- (void)btn1Clicked:(UIButton *)btn{
NSLog(@"btn1");
}
- (void)btn2Clicked:(UIButton *)btn{
NSLog(@"btn2");
}
```