错误实现方法
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *cellID = @"DSTestTableViewCell";
DSTestTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (!cell) {
cell = [[DSTestTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
[[mcell.testBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
NSLog(@"click action");
}];
return cell;
}
上面这种方法,当有多条数据时、或刷新页面后,点击button会掉用很多次点击事件。
由于数据过多,或者刷新页面时,会启用UITableView的复用机制,点击事件会多次被注册。
ReactiveCocoa注册代码如下:
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
NSCParameterAssert(nextBlock != NULL);
RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
return [self subscribe:o];
}
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
NSCParameterAssert(subscriber != nil);
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
NSMutableArray *subscribers = self.subscribers;
@synchronized (subscribers) {
[subscribers addObject:subscriber];
}
return [RACDisposable disposableWithBlock:^{
@synchronized (subscribers) {
// Since newer subscribers are generally shorter-lived, search
// starting from the end of the list.
NSUInteger index = [subscribers indexOfObjectWithOptions:NSEnumerationReverse passingTest:^ BOOL (id<RACSubscriber> obj, NSUInteger index, BOOL *stop) {
return obj == subscriber;
}];
if (index != NSNotFound) [subscribers removeObjectAtIndex:index];
}
}];
}
查看ReactiveCocoa代码可以发现,RACSignal
的subscribeNext
的实现是一种订阅者模式
,每次注册都会添加一个订阅者信号量[subscribers addObject:subscriber];
,一但触发点击事件,所有订阅者的信号量都会被触发,从而触发多次点击事件。
解决的办法就是避免多次注册点击事件
。
正确的实现方法
方法一
将注册点击事件,放到初始化cell中。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *cellID = @"DSTestTableViewCell";
DSTestTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (!cell) {
cell = [[DSTestTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
[[mcell.testBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
NSLog(@"click action");
}];
}
return cell;
}
方法二
UITableViewCell中定义block,实现UIButton点击事件,通过block传递事件。
[[_testBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
if (self.controlClickBlock) {
self.controlClickBlock
}
}];
在UITableViewCell初始化时,实现block。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *cellID = @"DSTestTableViewCell";
DSTestTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (!cell) {
cell = [[DSTestTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
mcell.controlClickBlock = ^() {
NSLog(@"click action");
};
return cell;
}
方法三
使用rac_command实现点击事件。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *cellID = @"DSTestTableViewCell";
DSTestTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (!cell) {
cell = [[DSTestTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
@weakify(self)
mcell.testBtn.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
@strongify(self)
NSLog(@"click action");
return [RACSignal empty];
}];
return cell;
}
方法四(推荐)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *cellID = @"DSTestTableViewCell";
DSTestTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (!cell) {
cell = [[DSTestTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
[[[mcell.testBtn rac_signalForControlEvents:UIControlEventTouchUpInside] takeUntil:mcell.rac_prepareForReuseSignal] subscribeNext:^(id x) {
NSLog(@"click action");
}];
return cell;
}