CFRunloop 优化TableView加载高清大图UI卡顿问题。单独分批加载

来源:互联网 发布:嘉嘉购物业软件 编辑:程序博客网 时间:2024/04/28 04:57

TableView卡顿环境分析:

       tableView加载过多的高清大图,Runloop不只处理iOS事件,渲染图形也是runloop处理的。

      而渲染图形的UI操作必须在主线程中,不能开辟线程进行图形处理。

      在拖动tableView的时候,Runloop要处理拖动事件,还要处理过多图片渲染,而造成卡顿。


解决卡顿分析:

      1、Runloop在一次循环渲染图片过多,那就让Runloop一次处理一张图片

      2、将处理图片的代码放在block中,然后加入数组中,处理几次加入几次。

      3、我们只需要渲染,tableView显示的图片,显示图片有最大个数。移开屏幕或者不处理的从队列数组里删去。



2和3其实就是逻辑的问题,不赘述了,下面会给出demo源码。主要讲讲第一个问题,是处理卡顿的重点。


第一个问题实现代码如下:


#pragma mark  设置runloop监听



//这里面都是C语言 --添加一个监听者

-(void)addRunloopObserver{

    //获取当前runloop

    CFRunLoopRef  currentRunloop = CFRunLoopGetCurrent();

    //runloop观察者上下文,为下面创建观察者准备,只有创建上下文才能在回调了拿到self对象,才能进行我们的逻辑操作.这是一个结构体。

    /**

     typedef struct {

     CFIndex version;

     void * info;

     const void *(*retain)(const void *info);

     void (*release)(const void *info);

     CFStringRef (*copyDescription)(const void *info);

     } CFRunLoopObserverContext;

     **/

    CFRunLoopObserverContext  context = {

        0,

        (__bridgevoid *)(self),

        &CFRetain,

        &CFRelease,

        NULL

    };

    //创建Runloop观察者  kCFRunLoopBeforeWaiting 观察在等待状态之前  runloop有下面几种状态看英文应该知道了。

    /*

     kCFRunLoopEntry = (1UL << 0),

     kCFRunLoopBeforeTimers = (1UL << 1),

     kCFRunLoopBeforeSources = (1UL << 2),

     kCFRunLoopBeforeWaiting = (1UL << 5),

     kCFRunLoopAfterWaiting = (1UL << 6),

     kCFRunLoopExit = (1UL << 7),

     kCFRunLoopAllActivities = 0x0FFFFFFFU

     */

    staticCFRunLoopObserverRef  obserberRef;

    obserberRef =CFRunLoopObserverCreate(NULL,kCFRunLoopBeforeWaiting,YES, 0,&callback, &context);

    //给当前runloop添加观察者

    CFRunLoopAddObserver(currentRunloop, obserberRef,kCFRunLoopDefaultMode);

    //释放观察者

    CFRelease(obserberRef);

}


//观察回调

static void callback(CFRunLoopObserverRef observer,CFRunLoopActivity activity,void *info){

    ViewController * vcSelf = (__bridgeViewController *)(info);

    

    if (vcSelf.TaskMarr.count >0) {

        //获取一次数组里面的任务并执行

        runloopTask  task  =  vcSelf.TaskMarr.firstObject;

        task();

        [vcSelf.TaskMarrremoveObjectAtIndex:0];

    }else{

        return;

    }

}



代码分析:

  • 先推理下,如果我要把任务放到Runloop里操作,首先我要获取Runloop。
  • 然后我们需要一个观察者,找一个时机把任务放进去。如果对runloop了解的话,会想到CFRunLoopObserver.然后我们创建一下这个观察者对象。 
  • 然后把观察者扔进runloop,这样我们就能拿到,Runloop等待之前的回调。
  • 然后把任务扔到回调中。


注意:这样还没有结束,还有一个问题待解决。那就是runloop运行一次任务就会休眠了。不会把你数组队列的任务进行处理。所以你要让Runloop不断的运行,直到你的任务结束。

     

方案如下:


//runloop一个事件源,让Runloop不断的运行执行代码块任务。

    [NSTimerscheduledTimerWithTimeInterval:0.1target:selfselector:@selector(runloopalive)userInfo:nilrepeats:YES];

//如果方法里什么都不干,APP性能影响并不大。但cpu增加负担,

-(void)runloopalive{

   //什么都不干

}



效果如下:




   demo地址:https://github.com/RainManGO/tableView-Caton-optimization




阅读全文
0 0
原创粉丝点击