大数据压力的处理方案

当设计到大量数据的时候,比如说,直播的聊天。我们处理的时候,可以考虑三点:

  1. 分多次处理
  2. kCFRunLoopBeforeWaiting 闲时处理(在进入等待前,也就是说现在已经不忙了,才会要进入等待)
  3. 频率问题

代码如下:

#import "TwoLargeDataVC.h"

@interface TwoLargeDataVC (){
    
    CFRunLoopObserverRef observerRef;
}

@property (nonatomic, strong)IBOutlet UITableView *tableView;
@property (nonatomic, strong)NSMutableArray *dataArr;
@property (nonatomic, strong)NSMutableArray *newsDataArr;
@property (nonatomic, strong)NSLock *dataLock;

@end


/*
  被动接受数据()
 */

@implementation TwoLargeDataVC

- (void)viewDidLoad {
    
    [super viewDidLoad];
    
    // 监听runloop状态 当runloop时候kCFRunLoopBeforeWaiting状态(开始等待前,即这个时候RunLoop已经不忙了),再来处理这个大数据量
    
    _dataArr = [NSMutableArray array];
    _newsDataArr = [NSMutableArray array];
    _dataLock = [NSLock new];

    _tableView.estimatedRowHeight = 0;
    _tableView.clipsToBounds = YES;
    [_tableView setFrame:CGRectMake(0, 300, _tableView.frame.size.width, 300)];
    
    [self runloopObserver];
    
    // 开启一个线程接收服务器的数据
    [NSThread detachNewThreadSelector:@selector(threadDataFromServer) toTarget:self withObject:nil];

}

/*
 1 分多次处理
 2 kCFRunLoopBeforeWaiting 闲时处理
 3 频率问题
 */

// 创建runloop 监听
- (void)runloopObserver{
    
    
    __weak typeof(self) weakSelf = self;
    __block NSTimeInterval timeInterVal = [[NSDate date] timeIntervalSince1970];
    // 创建观察者
    observerRef = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        
        // 设置刷新频率 最少0.5秒刷新一次
        //来处理数据量 (直播刷留言)
        if (activity == kCFRunLoopBeforeWaiting) { // 开始等待前,即这个时候RunLoop已经不忙了
            
            // 控制频率
            NSTimeInterval currentTimeInterVal = [[NSDate date] timeIntervalSince1970];
            // 小于0.5秒就直接返回,不进行后面的操作
            if (currentTimeInterVal - timeInterVal < 0.5) {
                return ;
            }
            timeInterVal = currentTimeInterVal;
            
            // 处理数据的时候要加上锁,保证数据安全
            [weakSelf.dataLock lock];
            NSArray *subArr = nil;
            if (weakSelf.newsDataArr.count > 0) { // 一次最多拿10条数据
                // 频率可以通过newsDataArr的数据量来控制(如果数据量大,频率就可以设置快一点,如果数据量小,就可以设置慢一点)
                NSRange range;
                if (weakSelf.newsDataArr.count >= 10) {
                    range = NSMakeRange(0, 10);
                }else{
                    range = NSMakeRange(0, weakSelf.newsDataArr.count-1);
                }
                // 拿出数据subArr
                subArr = [weakSelf.newsDataArr subarrayWithRange:range];
                // 将拿出的数据subArr从新数据中移除,避免下次还是会取到它
                [weakSelf.newsDataArr removeObjectsInRange:range];
                
            }
            
            // 将拿到的数据subArr添加到数据源中
            [weakSelf.dataArr addObjectsFromArray:subArr];
            [weakSelf.dataLock unlock];  // 数据操作结束,解锁
            [weakSelf.tableView reloadData]; // 刷新界面
            [weakSelf.tableView layoutIfNeeded];
            // 始终展示最后一页的数据,即最新的数据 tableView.contentSize.height 会每次都变大。所以ContentOffset的位置也要调整
            [weakSelf.tableView setContentOffset:CGPointMake(0, weakSelf.tableView.contentSize.height - weakSelf.tableView.frame.size.height)];
        }
        
    });
    // 添加观察者
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observerRef, kCFRunLoopCommonModes);
    
    
    
}

- (void)viewWillDisappear:(BOOL)animated{
    
    [super viewWillDisappear:animated];
    // 移除观察者
    CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observerRef, kCFRunLoopCommonModes);
    
}

// 接收服务器数据
- (void)threadDataFromServer{
    
    // 要注释了这个方法才会执行dealloc方法,因为下面是while(1),线程永远不会结束
    // 换成正常的网络请求,当请求结束,线程也就会结束的
    
    static int __countMsg = 0;
    while (1) {
        sleep(1);
        [_dataLock lock];
        for (int i = 0; i < random()%1000; i++) { // 生成1到999条不等的数据量

            __countMsg++;
            NSString *msgStr = [NSString stringWithFormat:@"%d---%ld", __countMsg, random()];
            [self.newsDataArr addObject:msgStr];
        }
        [_dataLock unlock];
        // 数据请求完成过后,要激活RunLoop来处理,这样才能将线程和RunLoop关联起来
        CFRunLoopWakeUp(CFRunLoopGetMain());
    }

}


#pragma mark - tableView delegate
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    
    return _dataArr.count;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    
    return 30;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
    }
    cell.textLabel.text = _dataArr[indexPath.row];
    return cell;
}

- (void)dealloc{
    
    NSLog(@"%s", __func__);
}

@end

上面需要注意的是:

  1. 处理数据的时候,要加锁。避免数据出问题。
  2. 使用多线程请求数据的时候,当请求完成过后要唤醒主RunLoop
  3. 页面消失的时候,要移除观察者

你可能感兴趣的:(大数据压力的处理方案)