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");
}
```