ios进阶教程-如何使用RUNLOOP优化大图加载?
程序员文章站
2024-01-22 08:59:40
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 是 睡眠 唤醒 一个循环结束
我们他runloop不休眠 一直在即将睡眠的时候唤醒
上一篇: ASP.NET 全局异常处理的方法
下一篇: iOS进阶教程-Block内部修改变量