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

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,

        (__bridge void *)(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

     */

    static CFRunLoopObserverRef  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 = (__bridge ViewController *)(info);

    

    if (vcSelf.TaskMarr.count > 0) {

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

        runloopTask  task  =  vcSelf.TaskMarr.firstObject;

        task();

        [vcSelf.TaskMarr removeObjectAtIndex:0];

    }else{

        return;

    }

}



代码分析:

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


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

     

方案如下:


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

    [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(runloopalive) userInfo:nil repeats:YES];

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

-(void)runloopalive{

   //什么都不干

}



效果如下:

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


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




你可能感兴趣的:(iOS,RunLoop,iOS,Runloop,Runtime实战)