NSTimer在tableView中的复用问题

来源:互联网 发布:人工智能仿生眼 编辑:程序博客网 时间:2024/05/21 14:04

之前,有位朋友,在写商品倒计时是,因为定时器在tableview上总是被复用,使改界面拖延了很久,最好找我写了个demo才解决。之后,还有不少iOS的朋友遇到类似复用的问题。这里只讲我这个demo中的关键代码及对复用的理解,demo的下载链接在文章最后。(如有不足请提出)


首先讲讲tableview的复用:cell在创建的时候,只会创建设备屏幕可见的cell个数,而不是按照你的数据源中的数据个数来创建的。然后反复使用这些cell,这样可以占用少量的内存来达到需要的效果。

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{        static NSString * ID = @"3829472938";    TableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:ID];    if (!cell) {                      cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];      }    [cell setDataWithTitle:_array[indexPath.row]];    return cell;}
这段代码,相信大家再熟悉不过了,但是不是所有人都理解这段代码。

static NSString * ID = @"3829472938";    TableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:ID];
可以理解成为创建的cell一个标记

if (!cell) {                      cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];      }
是加入cell为空,就创建新的cell,同时给占用这个内存的cell一个标记,然后再给cell赋值。而最重要的就是赋值这一步。因为,当你在拖动tableview的时候,加入该创建的cell都创建完了,那么就需要通过原有的cell来为需要的新的cell来赋值,通过赋值来改变cell上需要显示的数据,很多复用的问题就是因为没有及时的更新对应的数据。特别是在对待一些动态数据上,很多人在赋值一次以后,没有再次更新数据源,于是复用时,就重新赋上了数据源中的初始数据,造成显示数据不正确或者复用的问题。

下面是demo的核心代码:

控制器中定时器的创建

//创建定时器,控制数据源中的倒计时在controller中与cell的分类中一致-(void)viewDidAppear:(BOOL)animated{    [super viewDidAppear:animated];      //子线程创建定时器    [NSThread detachNewThreadSelector:@selector(multiThread) toTarget:self withObject:nil];}-(void)multiThread{    //确认定时器不是在主线程上时,创建定时器    //如果定时器要在子线程上跑起来,需要添加runloop    if (![NSThread isMainThread]) {        _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(countdown:) userInfo:nil repeats:YES];                NSRunLoop * runloop = [NSRunLoop currentRunLoop];        [runloop addTimer:_timer forMode:NSRunLoopCommonModes];        [runloop run];    }    }
主要用来更新数据源对应的数据
-(void)countdown:(NSTimer *)timer{    //遍历数据源,如果对应下标的元素是NSNumber类型,就-1    for (int i = 0; i < _array.count; i++) {        id obj = [_array objectAtIndex:i];        if ([obj isKindOfClass:[NSNumber class]]) {            int a = [obj intValue];            a -- ;            if (a <= -1) {                [_array replaceObjectAtIndex:i withObject:@"被点击"];            }else{                [_array replaceObjectAtIndex:i withObject:@(a)];            }        }    }}
自定义cell中button的定时器

//按钮点击事件-(void)clicked:(UIButton *)button{//    button.selected = YES;    if (_countDown==0) {        _countDown = 20;    }    [_delegate takeTimeToControllerWithTime:_countDown Button:button];    //子线程创建定时器    [NSThread detachNewThreadSelector:@selector(multiThread) toTarget:self withObject:nil];}-(void)setDataWithTitle:(id)title{    //赋值时判断传进来的参数类型,如果是模型或字典就没必要这样判断了    if ([title isKindOfClass:[NSNumber class]]) {        _countDown = [title intValue];//        [self clicked:_button];        if (_countDown==0) {            _countDown = 5;//自己改成120        }        //子线程创建定时器        [NSThread detachNewThreadSelector:@selector(multiThread) toTarget:self withObject:nil];    }else{        if (_timer.valid) {            //这里一定要判断,当title是字符串的时候,如果定时器还开着,一定要关,防止内存泄露和线程卡死,只要销毁定时器,runloop就会从线程上休眠            [self stop];        }        [_button setTitle:title forState:UIControlStateNormal];    }}-(void)multiThread{    //确认定时器不是在主线程上时,创建定时器    //如果定时器要在子线程上跑起来,需要添加runloop    if (![NSThread isMainThread]) {        _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(countdown:) userInfo:nil repeats:YES];                NSRunLoop * runloop = [NSRunLoop currentRunLoop];        [runloop addTimer:_timer forMode:NSRunLoopCommonModes];        [runloop run];    }    }//定时器倒计时-(void)countdown:(id)send{    [_button setTitle:[NSString stringWithFormat:@"%d", _countDown] forState:UIControlStateNormal];    _countDown--;    if (_countDown <= -1) {        //当倒计时结束时,改变按钮标题        [_timer invalidate];        [_button setTitle:@"已点击" forState:UIControlStateNormal];    }}
然后是最关键的一部,将点击的cell上的button改变的值存储到数据源中
//自定义cell的代理方法-(void)takeTimeToControllerWithTime:(int)time Button:(UIButton *)button{    //获取点击后的cell的下标,改变对应的数据源中的数据    TableViewCell *selectedCell = (TableViewCell *)button.superview.superview;    NSIndexPath *index = [_tableView indexPathForCell:selectedCell];    [_array replaceObjectAtIndex:index.row withObject:@(time)];    }



详细代码请参考下面的demo:

点击打开链接

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 微信gps信号不好怎么办 苹果6s定位不准怎么办 电脑不读取u盘怎么办 注塑机上的料烤坨了怎么办 智能锁电机坏了怎么办 注塑机加热嘴内扣突了怎么办 tpu粘在螺杆上怎么办 注塑机锁模时会有射退动作怎么办 电动车刹车油泵不打油怎么办 cad转pdf颜色浅怎么办 松下多功能传真一体机卡纸怎么办 无刷电机坏了怎么办 6kv高压电机绝缘不合格怎么办? 400t油压机下降太慢怎么办 无法连线到服务器1~1怎么办? 数控车床车角度不亮怎么办 超市存包柜的票不见了怎么办 交货期来不及导致船期延误怎么办 跑1000米中途累怎么办 手指被机器压烂怎么办 机械手不能回归原点该怎么办 前缘送纸纸板翘怎么办 三菱AL 1R.2报警怎么办 工作好但领导不好伺候怎么办 孕妇憋尿憋的小腹疼怎么办 怀孕憋尿憋的小腹疼怎么办 半夜憋尿憋的小腹疼怎么办 新生儿大便次数较多怎么办 母猎生下三天没有奶怎么办 孩孑大便干不爱喝水怎么办 发那科1050报警怎么办 plc模块bf亮了怎么办 plc模块df亮了怎么办 西门子触摸屏进不了主画面怎么办 pos机显示白屏怎么办 洗衣机的门坏了怎么办 西门子洗衣机门坏了怎么办 拆线线断在肉里怎么办 总线绝对值编码器方向反了怎么办 台达伺服报警009怎么办 台达plc禁止上传怎么办?