当前位置: 移动技术网 > 移动技术>移动开发>IOS > ios进阶教程-如何使用RUNLOOP优化大图加载?

ios进阶教程-如何使用RUNLOOP优化大图加载?

2018年10月05日  | 移动技术网移动技术  | 我要评论

1 为什么要优化大图加载

大图片在渲染的时候,比较耗费时间
我们利用runloop 来优化:

思路:1.每一次runloop,都只加载一个小任物,把图片任务放到数组,从数组循环来加载.这样可以是项目达到流畅.
2.只加载当前视图内的图片任务
3.为了不让runloop休眠.我们要用一个timer区持有这个runloop 或者 通知注册runloop事件,让快要睡眠的时候去执行唤醒.

直接贴代码

#import "dwurunloopworkdistribution.h"
#import 

#define dwurunloopworkdistribution_debug 1

@interface dwurunloopworkdistribution ()

@property (nonatomic, strong) nsmutablearray *tasks;

@property (nonatomic, strong) nsmutablearray *taskskeys;

@property (nonatomic, strong) nstimer *timer;

@end

@implementation dwurunloopworkdistribution

- (void)removealltasks {
    [self.tasks removeallobjects];
    [self.taskskeys removeallobjects];
}

- (void)addtask:(dwurunloopworkdistributionunit)unit withkey:(id)key{
    //将任务对应的添加到task 和 taskkeys数组数组中方便在注册方法中的callback中以待处理
    [self.tasks addobject:unit];
    [self.taskskeys addobject:key];
   // nslog(@"%@%@",unit,key);
    if (self.tasks.count > self.maximumqueuelength) {
        [self.tasks removeobjectatindex:0];
        [self.taskskeys removeobjectatindex:0];
    }
}

- (void)_timerfiredmethod:(nstimer *)timer {
    //we do nothing here
}

- (instancetype)init
{
    if ((self = [super init])) {
        _maximumqueuelength = 30;
        _tasks = [nsmutablearray array];
        _taskskeys = [nsmutablearray array];
      //  _timer = [nstimer scheduledtimerwithtimeinterval:0.1 target:self selector:@selector(_timerfiredmethod:) userinfo:nil repeats:yes];

    }
    return self;
}

+ (instancetype)sharedrunloopworkdistribution {

    static dwurunloopworkdistribution *singleton;
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        singleton = [[dwurunloopworkdistribution alloc] init];
        [self _registerrunloopworkdistributionasmainrunloopobserver:singleton];
    });
    return singleton;
}


+ (void)_registerrunloopworkdistributionasmainrunloopobserver:(dwurunloopworkdistribution *)runloopworkdistribution {
    static cfrunloopobserverref defaultmodeobserver;


    //kcfrunloopbeforewaiting  runloop 进入休眠的时候唤醒
    _registerobserver(kcfrunloopbeforewaiting, defaultmodeobserver, nsintegermax - 999, kcfrunloopdefaultmode, (__bridge void *)runloopworkdistribution, &_defaultmoderunloopworkdistributioncallback);
    /*
     1. kcfrunloopdefaultmode: 默认 mode,通常主线程在这个 mode 下运行。

     2. uitrackingrunloopmode: 追踪mode,保证scrollview滑动顺畅不受其他 mode 影响。

     3. uiinitializationrunloopmode: 启动程序后的过渡mode,启动完成后就不再使用。

     4: gseventreceiverunloopmode: graphic相关事件的mode,通常用不到。

     5: kcfrunloopcommonmodes: 占位mode,作为标记defaultmode和commonmode用。

  */
}

// 验证下 这是什么调用方法  static
static void _registerobserver(cfoptionflags activities, cfrunloopobserverref observer, cfindex order, cfstringref mode, void *info, cfrunloopobservercallback callback) {
    cfrunloopref runloop = cfrunloopgetcurrent();
    cfrunloopobservercontext context = {
        0,
        info,
        &cfretain,
        &cfrelease,
        null
    };
//    switch (activities) {
//        case kcfrunloopentry:
//            nslog(@"即将进入runloop");
//            break;
//        case kcfrunloopbeforetimers:
//            nslog(@"即将处理timer");
//            break;
//        case kcfrunloopbeforesources:
//            nslog(@"即将处理input sources");
//            break;
//        case kcfrunloopbeforewaiting:
//            nslog(@"即将睡眠");
//            break;
//        case kcfrunloopafterwaiting:
//            nslog(@"从睡眠中唤醒,处理完唤醒源之前");
//            break;
//        case kcfrunloopexit:
//            nslog(@"退出");
//            break;
//    }
    observer = cfrunloopobservercreate(     null,
                                            activities,
                                            yes,
                                            order,
                                            callback,
                                            &context);
    cfrunloopaddobserver(runloop, observer, mode);

   //创建一个runloop观察者
   /*
    cfallocatorref allocator:内存分配
    cfoptionflags activities :唤醒标志
    boolean repeats:是否重复
    cfindex order :优先级
    cfrunloopobservercallback callout :回调 绑定
    cfrunloopobservercontext *context  :传递
    */
    cfrelease(observer);
}





//typedef void (*cfrunloopobservercallback)(cfrunloopobserverref observer, cfrunloopactivity activity, void *info);
static void _runloopworkdistributioncallback(cfrunloopobserverref observer, cfrunloopactivity activity, void *info)
{  //                             __bridge 类型 实现id类型与void*类型


    dwurunloopworkdistribution *runloopworkdistribution = (__bridge dwurunloopworkdistribution *)info;
    if (runloopworkdistribution.tasks.count == 0) {
        return;
    }
    bool result = no;
    while (result == no && runloopworkdistribution.tasks.count) {
        dwurunloopworkdistributionunit unit  = runloopworkdistribution.tasks.firstobject;
        result = unit();
        [runloopworkdistribution.tasks removeobjectatindex:0];
        [runloopworkdistribution.taskskeys removeobjectatindex:0];
    }
}


static void _defaultmoderunloopworkdistributioncallback(cfrunloopobserverref observer, cfrunloopactivity activity, void *info) {
    nslog(@"---------> callback唤醒回调 当前状态cfrunloopactivity[%lu]",activity);
    //cfrunloopobserverref 观察者
    //cfrunloopactivity    runloop状态  kcfrunloopbeforewaiting
    // 接下来执行 _runloopworkdistributioncallback
    _runloopworkdistributioncallback(observer, activity, info);


//    kcfrunloopentry         = (1ul << 0), // 即将进入loop
//    kcfrunloopbeforetimers  = (1ul << 1), // 即将处理 timer
//    kcfrunloopbeforesources = (1ul << 2), // 即将处理 source
//    kcfrunloopbeforewaiting = (1ul << 5), // 即将进入休眠
//    kcfrunloopafterwaiting  = (1ul << 6), // 刚从休眠中唤醒
//    kcfrunloopexit          = (1ul << 7), // 即将退出loop

}
@end

@implementation uitableviewcell (dwurunloopworkdistribution)

@dynamic currentindexpath;

- (nsindexpath *)currentindexpath {
    nsindexpath *indexpath = objc_getassociatedobject(self, @selector(currentindexpath));
    return indexpath;
}

- (void)setcurrentindexpath:(nsindexpath *)currentindexpath {
    objc_setassociatedobject(self, @selector(currentindexpath), currentindexpath, objc_association_retain_nonatomic);
    //这四个后面的参数分别表示:源对象,关键字,关联的对象和一个关联策略。
}

.h 文件

#import 

typedef bool(^dwurunloopworkdistributionunit)(void);

@interface dwurunloopworkdistribution : nsobject

@property (nonatomic, assign) nsuinteger maximumqueuelength;

+ (instancetype)sharedrunloopworkdistribution;
- (void)addtask:(dwurunloopworkdistributionunit)unit withkey:(id)key;

- (void)removealltasks;

@end

@interface uitableviewcell (dwurunloopworkdistribution)

@property (nonatomic, strong) nsindexpath *currentindexpath;

@end

代码分析来源于:
推荐一个第三方runloopworkdistribution,地址https://github.com/diwu/runloopworkdistribution

我们可以添加观察者 监听runloop的循环
// 添加一个监听者

- (void)addobserver {

    // 1. 创建监听者
    /**
     *  创建监听者
     *
     *  @param allocator#>  分配存储空间
     *  @param activities#> 要监听的状态
     *  @param repeats#>    是否持续监听
     *  @param order#>      优先级, 默认为0
     *  @param observer     观察者
     *  @param activity     监听回调的当前状态
     */
    cfrunloopobserverref observer = cfrunloopobservercreatewithhandler(kcfallocatordefault, kcfrunloopallactivities, yes, 0, ^(cfrunloopobserverref observer, cfrunloopactivity activity) {

        /*
         kcfrunloopentry = (1ul << 0),          进入工作
         kcfrunloopbeforetimers = (1ul << 1),   即将处理timers事件
         kcfrunloopbeforesources = (1ul << 2),  即将处理source事件
         kcfrunloopbeforewaiting = (1ul << 5),  即将休眠
         kcfrunloopafterwaiting = (1ul << 6),   被唤醒
         kcfrunloopexit = (1ul << 7),           退出runloop
         kcfrunloopallactivities = 0x0fffffffu  监听所有事件
         */
        //nslog(@"%@", [nsrunloop currentrunloop].currentmode); // 查看当前的runloop运行状态
        switch (activity) {
            case kcfrunloopentry:
                nslog(@"runloop--进入");
                break;
            case kcfrunloopbeforetimers:
                nslog(@"runloop--timer事件");
                break;
            case kcfrunloopbeforesources:
                nslog(@"runloop--source事件");
                break;
            case kcfrunloopbeforewaiting:
                nslog(@"runloop--休眠");
                break;
            case kcfrunloopafterwaiting:
                nslog(@"runloop--唤醒");
                break;
            case kcfrunloopexit:
                nslog(@"runloop--退出");
                break;
            default:
                break;
        }
    });

    // 2. 添加监听者
    /**
     *  给指定的runloop添加监听者
     *
     *  @param rl#>       要添加监听者的runloop
     *  @param observer#> 监听者对象
     *  @param mode#>     runloop的运行模式, 填写默认模式即可
     */
    cfrunloopaddobserver(cfrunloopgetcurrent(), observer, kcfrunloopdefaultmode);
}
- (void)viewdidload {
    [super viewdidload];
    [self addobserver]; // 添加监听者

}

看log
正常的runloop 是 睡眠 唤醒 一个循环结束
这里写图片描述

我们他runloop不休眠 一直在即将睡眠的时候唤醒
这里写图片描述

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网