美文网首页
iOS大量数据倒计时及可见的数据才轮询

iOS大量数据倒计时及可见的数据才轮询

作者: 独孤流 | 来源:发表于2017-08-01 14:27 被阅读482次
    ![WX20170810-151337.png](https://img.haomeiwen.com/i1605558/90e68cb0012df555.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    前言:
    在开发中遇到这么一个需求,一个列表页展示很多商品数据,这些商品数据的信息是在实时变动的,可以简单的说是一个TableView的Cell里每个Cell是一个商品,每个商品的数据通过接口轮训数据并
    实时展示显示的数据,同时还要保证离开这个页面后停止无意义的数据变换及轮询,减少不必要的服务器带宽
    知识点:Cell重用、定时器、通知、网络请求、倒计时、轮询可见部分
    过程:
    查找了很多资料,在参考了iOS 在cell中使用倒计时的处理方法这篇博文后找了思路
    1、由于Cell太多,不能每个Cell都创建一个Timer刷定时器,而且也不好控制让离开改某个View时停止定时器,在进入该页面后又重新开始定时器,所以需要用到的页面,在对应的Controller里使用一个定时器统一管理
    // .h
    @interface LHCountDownManager : NSObject
    // 倒计时通知名字
    @property (nonatomic,copy) NSString  *countDownNotifyKey;
    /// 时间差(单位:秒)
    @property (nonatomic, assign) NSInteger timeInterval;
    
    /// 开始倒计时
    - (void)start;
    /// 刷新倒计时
    - (void)reload;
    /// 停止倒计时
    - (void)invalidate;
    
    + (instancetype)managerWithCountDownKey:(NSString *)countDownKey;
    @end
    
    // .m
    #import "LHCountDownManager.h"
    @interface LHCountDownManager()
    @property (nonatomic, strong) NSTimer *timer;
    @end
    @implementation LHCountDownManager
    + (instancetype)managerWithCountDownKey:(NSString *)countDownKey
    {
        LHCountDownManager *manager = [LHCountDownManager new];
        manager.countDownNotifyKey = countDownKey;
        return manager;
    }
    - (void)start {
        // 启动定时器
        [self timer];
    }
    
    - (void)reload {
        // 刷新只要让时间差为0即可
        _timeInterval = 0;
    }
    
    - (void)invalidate {
        [self.timer invalidate];
        self.timer = nil;
        self.timeInterval = 0;
    }
    
    - (void)timerAction {
        // 时间差+1
        self.timeInterval ++;
        
        // 发出通知--可以将时间差传递出去,或者直接通知类属性取
        [[NSNotificationCenter defaultCenter] postNotificationName:self.countDownNotifyKey object:nil userInfo:@{@"TimeInterval" : @(self.timeInterval)}];
        // 对最大值进行限制
        if (self.timeInterval >= NSUIntegerMax) {
            self.timeInterval = 0;
        }
        // 对最小值进行限制
        if (self.timeInterval <= 0) {
            self.timeInterval = 0;
        }
    }
    - (NSTimer *)timer {
        if (_timer == nil) {
            _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
            [[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
        }
        return _timer;
    }
    @end
    
    

    2、由统一管理定时器的类进行按一定时间倒计时发通知,然后由有需要变动的Cell进行接收通知并处理,如果Cell接收到的通知后进行对Cell管理的model的值如果有变化,进行倒计时显示

    #pragma mark - ******** 倒计时通知处理
    - (void)setCountDownNotifyName:(NSString *)countDownNotifyName
    {
        // 如果以前设置的倒计时通知有变化,去除以前的通知
        if (_countDownNotifyName != nil && ![_countDownNotifyName isEqualToString:countDownNotifyName]) {
            [[NSNotificationCenter defaultCenter] removeObserver:self name:_countDownNotifyName object:nil];
        }
        // 如果现在设置的通知与之前的不一致,添加通知
        if (countDownNotifyName !=nil && ![_countDownNotifyName isEqualToString:countDownNotifyName]) {
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(countDownChange:) name:countDownNotifyName object:nil];
        }
        _countDownNotifyName = countDownNotifyName;
    }
    #pragma mark 显示倒计时减一
    - (void)countDownChange:(NSNotification *)notify
    {
        // 倒计时减一
        self.model.goodsTime --;
        NSInteger countDown = self.model.goodsTime;
        /// 当倒计时到了进行回调
        if (countDown <= 0) {
            countDown = 0;
        }
        NSString *cuountDownText = [NSString stringWithFormat:@"%02zd:%02zd:%02zd", countDown/3600, (countDown/60)%60, countDown%60];
        self.timeLbl.text = cuountDownText;
    }
    #pragma mark - ******** 轮询价格变化处理
    - (void)setPriceChangeNotifyName:(NSString *)priceChangeNotifyName
    {
        // 如果以前设置的倒计时通知有变化,去除以前的通知
        if (_priceChangeNotifyName != nil && ![_priceChangeNotifyName isEqualToString:priceChangeNotifyName]) {
            [[NSNotificationCenter defaultCenter] removeObserver:self name:_priceChangeNotifyName object:nil];
        }
        // 如果现在设置的通知与之前的不一致,添加通知
        if (priceChangeNotifyName !=nil && ![_priceChangeNotifyName isEqualToString:priceChangeNotifyName]) {
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(priceChange:) name:priceChangeNotifyName object:nil];
        }
        _priceChangeNotifyName = priceChangeNotifyName;
    }
    #pragma mark 内容经过轮询后显示最新内容
    - (void)priceChange:(NSNotification *)notify
    {
        NSDictionary *userInfo = notify.userInfo;
        NSString *goodsId = userInfo[@"goodsId"];
        // goodsId等于空或不是当前Cell显示的model,直接不处理
        if (goodsId == nil || [goodsId isEqualToString:@""] || ![goodsId isEqualToString:self.model.goodsId]) {
            return;
        }
        
        NSString *currentNamePrice = [NSString stringWithFormat:@"%@出价¥%.2f",self.model.goodsBuyer,self.model.goodsPrice];
        // 如果购买者或者购买的价格没有变化不处理
        if (currentNamePrice == nil || [self.oldNamePrice isEqualToString:currentNamePrice]) {
            return;
        }
        // 更新数据
        self.peoplePriceLbl.text = currentNamePrice;
        NSInteger countDown = self.model.goodsTime;
        /// 当倒计时到了进行回调
        if (countDown <= 0) {
            countDown = 0;
            
        }
        NSString *cuountDownText = [NSString stringWithFormat:@"%02zd:%02zd:%02zd", countDown/3600, (countDown/60)%60, countDown%60];
        self.timeLbl.text = cuountDownText;
        self.oldNamePrice = currentNamePrice;
        
    }
    - (void)dealloc
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    #pragma mark - ******** 设置数据
    - (void)setModel:(LHProductModel *)model
    {
        _model = model;
        [self.productImg sd_setImageWithURL:[NSURL URLWithString:model.goodsImage] placeholderImage:[UIImage imageNamed:@"nahan"]];
        self.nameLbl.text = model.goodsName;
        NSString *currentNamePrice = [NSString stringWithFormat:@"%@出价¥%.2f",self.model.goodsBuyer,self.model.goodsPrice];
        self.peoplePriceLbl.text = currentNamePrice;
        self.oldNamePrice = currentNamePrice;
        NSInteger countDown = model.goodsTime;
        /// 当倒计时到了进行回调
        if (countDown <= 0) {
            countDown = 0;
            
        }
        NSString *cuountDownText = [NSString stringWithFormat:@"%02zd:%02zd:%02zd", countDown/3600, (countDown/60)%60, countDown%60];
        self.timeLbl.text = cuountDownText;
    }
    
    

    3、如何轮询只显示的Cell的数据,我的办法是轮询也统一放在倒计时管理里处理,然后再Cell的 _setModel里把旧的model移除需要轮询的队列,把新要显示的model加入要轮询的队列,然后管理定时器的类会定时轮询这些model,这样一个定时器管理类就能把倒计时显示和最少量轮询同时完成
    3.1 、管理一个保持轮询中的数组pollingListM
    3.2、 在每次重新请求了数据后将pollingListM清空
    3.3、在cellForRowAtIndexPath方法里将被替换不显示的model移除需要轮询的数组,将接近显示的model放入轮询数组,然后通过定时器在列表里轮询列表数组

    #import "LHHomeViewController.h"
    #import "LHProductTableViewCell.h"
    #import "ProductService.h"
    #import "LHCountDownManager.h"
    #import "LHGoodsDetailController.h"
    
    #define kHomeCountDownTimerNotifiyKey @"kHomeCountDownTimerNotifiyKey"
    #define kHomeProductChangeNotifiyKey @"kHomeProductChnageNotifiyKey"
    
    @interface LHHomeViewController () <UITableViewDataSource,UITableViewDelegate>
    @property (nonatomic,strong) UITableView * tableView;
    @property (nonatomic,strong) ProductService * service;
    @property (nonatomic,strong) LHCountDownManager * countDownManager;
    @property (nonatomic,strong) NSMutableArray * pollingListM;
    @end
    
    @implementation LHHomeViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        [self.view addSubview:self.tableView];
        self.view.backgroundColor = [UIColor whiteColor];
        
        [self _loadFirstPage];
        [self _addNotifications];
    }
    - (void)_loadFirstPage
    {
        __weak typeof(self) weakSelf = self;
        [self.service loadFirstPageProductListWithBlock:^(BOOL success, NSString *msg, BOOL hasMore) {
            if (success) {
                [weakSelf.pollingListM removeAllObjects];
                // 有重新加载了数据就要将轮询数组里的数据全清空一遍
                [weakSelf.tableView reloadData];
            }
        }];
    }
    - (void)_addNotifications
    {
        // 添加定时器变化
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(countDownChange:)
                                                     name:kHomeCountDownTimerNotifiyKey
                                                   object:nil];
    }
    #pragma mark - ******** 轮询所有保存在轮询数组里的内容
    - (void)countDownChange:(NSNotification *)notify
    {
        // 2秒轮询一次
        if (self.countDownManager.timeInterval % 2 == 0) {
            NSString *ids = [[self.pollingListM valueForKeyPath:@"goodsId"] componentsJoinedByString:@","];
            NSLog(@"变量的数组:%@",ids);
            // 对所有轮询数组里的数据进行轮询请求
            for (LHProductModel *model in self.pollingListM) {
                // 这个方法只改变model的值,不重新创建model
                [self.service pollingModelWithModel:model block:^(BOOL success, id msg) {
                    // 如果请求成功,发送通知修改显示内容
                    if (success && [msg isKindOfClass:[LHProductModel class]]) {
                        [[NSNotificationCenter defaultCenter] postNotificationName:kHomeProductChangeNotifiyKey object:nil userInfo:@{@"goodsId":((LHProductModel *)msg).goodsId?:@""}];
                    }
                }];
            }
        }
    }
    #pragma mark - ******** 进入页面或离开页面开启关闭定时器
    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
        [self.countDownManager start];
    }
    - (void)viewWillDisappear:(BOOL)animated
    {
        [super viewWillDisappear:animated];
        [self.countDownManager invalidate];
    }
    - (void)dealloc
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    #pragma mark - ******** UITableViewDataSource && UITabelViewDelegate
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    {
        return 1;
    }
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        NSInteger count = self.service.dataList.count;
        return count;
    }
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        return 130;
    }
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        LHProductModel *model = self.service.dataList[indexPath.row];
        LHProductTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"LHProductTableViewCell"];
        // 将重用Cell之前保存的model从轮询数组里去除
        if (cell.model != nil) {
            [self.pollingListM removeObject:cell.model];
        }
        // 将接近要显示的Cell保存的model放到轮询数组里用于轮询
        if (model != nil) {
            [self.pollingListM addObject:model];
        }
        // 设置让Cell可以处理倒计时的通知
        cell.countDownNotifyName = kHomeCountDownTimerNotifiyKey;
        cell.priceChangeNotifyName = kHomeProductChangeNotifiyKey;
        // 将数据赋值给Cell显示
        cell.model = model;
        return cell;
    }
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [tableView deselectRowAtIndexPath:indexPath animated:YES];
        });
        LHProductModel *model = self.service.dataList[indexPath.row];
        LHGoodsDetailController *vc = [LHGoodsDetailController new];
        vc.model = model;
        vc.hidesBottomBarWhenPushed = YES;
        [self.navigationController pushViewController:vc animated:YES];
    }
    #pragma mark - ******** getter && setter
    - (UITableView *)tableView
    {
        if (_tableView == nil) {
            _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
            _tableView.dataSource = self;
            _tableView.delegate = self;
            [_tableView registerNib:[UINib nibWithNibName:@"LHProductTableViewCell" bundle:nil] forCellReuseIdentifier:@"LHProductTableViewCell"];
        }
        return _tableView;
    }
    - (ProductService *)service
    {
        if (_service == nil) {
            _service = [ProductService new];
        }
        return _service;
    }
    - (LHCountDownManager *)countDownManager
    {
        if (_countDownManager == nil) {
            _countDownManager = [LHCountDownManager managerWithCountDownKey:kHomeCountDownTimerNotifiyKey];
        }
        return _countDownManager;
    }
    - (NSMutableArray *)pollingListM
    {
        if (_pollingListM == nil) {
            _pollingListM = [NSMutableArray array];
        }
        return _pollingListM;
    }
    @end
    

    具体项目demo

    相关文章

      网友评论

          本文标题:iOS大量数据倒计时及可见的数据才轮询

          本文链接:https://www.haomeiwen.com/subject/xqujlxtx.html