欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2022-05-04 11:02:48
1 为什么要优化大图加载 大图片在渲染的时候,比较耗费时间 我们利用runloop 来优化: 思路:1.每一次runloop,都只加载一个小任物,把图片任务放到数组,从数组循环来加载.这样可以是项目...

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 是 睡眠 唤醒 一个循环结束
ios进阶教程-如何使用RUNLOOP优化大图加载?

我们他runloop不休眠 一直在即将睡眠的时候唤醒
ios进阶教程-如何使用RUNLOOP优化大图加载?