阅读 310

iOS--利用RunLoop优化加载表格

引子和思路

runloop是每次循环就会渲染屏幕上所有的点,当我们在做一个table上显示很多张图片时,拖动table,会感觉到明显的卡顿,就是因为每一次我们拖动table,runloop循环就会加载渲染一次,所以十分耗时,思路是我们分成图片数量次加载,每一次只加载一张图片,一次一次的加载。 demo在文章最下面放出。

实现流程

把每一次需要加载cell的代码包装成一个任务(block),放到数组里面。

//MARK: 添加任务
-(void)addTask:(RunloopBlock)unit withKey:(id)key{
    [self.tasks addObject:unit];
    [self.tasksKeys addObject:key];
    //保证之前没有显示出来的任务,不再浪费时间加载
    if (self.tasks.count > self.max) {
        [self.tasks removeObjectAtIndex:0];
        [self.tasksKeys removeObjectAtIndex:0];
    }
    
}
复制代码

然后写一个方法去监听runloop,每一次循环只在数组里面取一个任务去执行。我们需要用到CoreFoundation框架中的CFRunloop,找到其中的Observer观察者,每一条都代表着runloop的一种状态。

kCFRunLoopEntry  进入一次循环
kCFRunLoopBeforeTimers 处理timers之前
kCFRunLoopBeforeSources 处理sources之前
kCFRunLoopBeforeWaiting 即将进入睡眠状态
kCFRunLoopAfterWaiting 从睡眠状态即将处理事件
kCFRunLoopExit 退出
kCFRunLoopAllActivities 所有的事件
复制代码

了解了这些,开始进行监听runloop,并需要在viewdidload中启动观察者。

//这里面都是C语言 -- 添加一个监听者
-(void)addRunloopObserver{
    //获取当前的RunLoop
    CFRunLoopRef runloop = CFRunLoopGetCurrent();
    //定义一个context 上下文
    CFRunLoopObserverContext context = {
        0,
        ( __bridge void *)(self), // info 传入了self  下面获取当前控制器
        &CFRetain,
        &CFRelease,
        NULL
    };
    //定义一个观察者
    static CFRunLoopObserverRef defaultModeObsever;
    //创建观察者
    defaultModeObsever = CFRunLoopObserverCreate(NULL,
                                                 kCFRunLoopBeforeWaiting,
                                                 YES,
                                                 NSIntegerMax - 999,
                                                 &Callback,
                                                 &context
                                                 );
    
    //添加当前RunLoop的观察者
    CFRunLoopAddObserver(runloop, defaultModeObsever, kCFRunLoopDefaultMode);
    //c语言有creat 就需要release
    CFRelease(defaultModeObsever);
   
}
复制代码

由于runloop每次之行结束就会进入睡眠状态,但是我们需要让他一直不睡眠,一直执行,这里使用一个NSTimer,绑定一个事件,每0.1秒执行一次。事件什么都不需要做,只是为了让runloop一直都是唤醒状态。 接下来,需要定义上面的callback回调函数,并且每一次runloop循环只回调一次。

//MARK: 回调函数
//定义一个回调函数  一次RunLoop来一次
//  这三个参数  会默认传递给这个方法 根据这个info才能取到当前控制器
static void Callback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
    ViewController * vc = (__bridge ViewController *)(info);
    if (vc.tasks.count == 0) {
        return;
    }
    BOOL result = NO;
    while (result == NO && vc.tasks.count) {
        //取出任务
        RunloopBlock unit = vc.tasks.firstObject;
        //执行任务
        result = unit();
        //干掉第一个任务
        [vc.tasks removeObjectAtIndex:0];
        //干掉标示
        [vc.tasksKeys removeObjectAtIndex:0];
    }
}
复制代码

我们想要的基本效果就已经实现了。 我的臭屁小demo 国外的大佬demo

文章分类
iOS
文章标签