前语
信任每一个中高级iOS开发者即便不怎么深入了解过iOS的runLoop机制,可是都听过runLoop这玩意。平时查找中最多的用途就是用来监听卡顿啥的,可是你们是否知道咱们还能够用来做闲暇行列的使用呢?
一.场景
假如咱们需求下载一堆文件,可是又不是很紧迫,这时假如咱们挑选一次性全量下载,那么即便下载的操作放在了子线程,也会很多占用CPU资源,导致CPU使用率居高不下。而假如CPU使用率达到必定高度时,就无法在规则时间内完结画面的烘托,然后出现肉眼可见的卡顿。这时候咱们能够能够经过监听Runloop的闲暇状况来进行分批下载文件,每次循环进入闲暇状况咱们就去下载小部分文件,然后处理卡顿的问题。
二.实战
1.先构建了一个MyOperationQueue,用来做闲暇行列,贴出代码如下:
MyOperationQueue.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface MyOperation : NSObject
@end
@interface MyOperationQueue : NSObject
//单例
+ (instancetype)sharedObject;
//增加使命
- (void)addBeforeWaitingQueue:(dispatch_block_t )taskBlock;
@end
NS_ASSUME_NONNULL_END
MyOperationQueue.m
#include <pthread.h>
#import "MyOperationQueue.h"
//beforWaiting的回调
void MyOperationRunLoopObserverWaitingCallBack (CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
//操作类,只做赋值和回调
@interface MyOperation ()
@property (nonatomic, copy) void(^executeOperation)(MyOperation *operation);
//履行函数
- (void)execute;
@end
@implementation MyOperation
- (void)execute
{
if (self.executeOperation) {
self.executeOperation(self);
}
}
@end
#pragma mark - MyOperationQueue
typedef NS_ENUM(NSInteger, MyOperationQueueState) {
MyOperationQueueState_None,
MyOperationQueueState_Start,
MyOperationQueueState_Executing,
MyOperationQueueState_lastExecuteFinish,
MyOperationQueueState_Finish,
};
@interface MyOperationQueue() {
CFRunLoopObserverRef runLoopObserver;
}
@property (nonatomic, assign) MyOperationQueueState state;
@property (nonatomic, strong) NSMutableArray<MyOperation *> *queue;
@property (nonatomic, assign) int interval; // 每个operation最少间隔时间,单位ms
@property (nonatomic, assign) CFAbsoluteTime lastTime;
@end
@implementation MyOperationQueue
+ (instancetype)sharedObject
{
static dispatch_once_t onceToken;
static MyOperationQueue *shareObject;
dispatch_once(&onceToken, ^{
shareObject = [[MyOperationQueue alloc] init];
});
return shareObject;
}
- (void)dealloc
{
[self removeRunloopCallback];
}
- (instancetype)init
{
self = [super init];
if (self) {
_queue = [NSMutableArray new];
_lastTime = CFAbsoluteTimeGetCurrent() * 1000.0;//乘以1000,变成单位毫秒
_interval = 30;//30ms,1000ms=1s
[self initRunloopCallback];
}
return self;
}
- (void)initRunloopCallback
{
//创建上下文
CFRunLoopObserverContext context = {0, (__bridge void *)self, NULL, NULL, NULL};
//创建监听和回调
unLoopObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, YES, 0, &MyOperationRunLoopObserverWaitingCallBack, &context);
//增加监听,主线程回调
CFRunLoopAddObserver(CFRunLoopGetMain(), runLoopObserver, kCFRunLoopCommonModes);
}
//释放
- (void)removeRunloopCallback
{
self.state = MyOperationQueueState_None;
if (runLoopObserver) {
CFRunLoopRemoveObserver(CFRunLoopGetMain(), runLoopObserver, kCFRunLoopDefaultMode);
CFRelease(runLoopObserver);
}
}
//履行函数
- (void)executeOperation
{
switch (self.state) {
case MyOperationQueueState_None:
case MyOperationQueueState_Executing:
case MyOperationQueueState_AllFinish:
return;
default:
break;
}
CFAbsoluteTime curTime = CFAbsoluteTimeGetCurrent() * 1000;
if (curTime - _lastTime < _interval) {//30ms内不履行下一个使命
return;
}
_lastTime = curTime;
MyOperation *operation = _queue.firstObject;//取第一个去履行
if (operation) {
self.state = MyOperationQueueState_Executing;
NSLog(@"executeOperation 履行使命:%p",operation);
[operation execute];
}
}
//设置状况为开始
- (void)start
{
switch (_state) {
case MyOperationQueueState_Start:
case MyOperationQueueState_Executing:
return;
default:
break;
}
self.state = MyOperationQueueState_Start;
}
//完结了一个使命
- (void)onOperationFinish:(MyOperation *)operation
{
self.state = MyOperationQueueState_lastExecuteFinish;
[_queue removeObject:operation];
if ([_queue count] == 0) {
self.state = MyOperationQueueState_AllFinish;
}
}
#pragma mark - public metho
- (void)addBeforeWaitingQueue:(dispatch_block_t )taskBlock
{
MyOperation *task = [[MyOperation alloc] init];
task.executeOperation = ^(MyOperation *operation) {
taskBlock();
//设置该使命完结
[[MyOperationQueue sharedObject] onOperationFinish:operation];
};
if (![_queue containsObject:task]) {
[_queue addObject:task];
NSLog(@"增加使命:%p",task);
}
[self start];//开始
}
@end
//runLoop回调
void MyOperationRunLoopObserverWaitingCallBack (CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
MyOperationQueue *opQueue = (__bridge MyOperationQueue *) info;
if (opQueue) {
[opQueue executeOperation];//履行函数
}
}
2.调用方法,如下图
3.履行结果
4.代码说明
在MyOperationQueue
这个单例里边,我创建了一个寄存使命的行列queue,每往里边增加一次使命,就把使命增加进我的行列,然后把当时的状况设置成才开始状况(假如当时是履行使命中的状况,就不会设置成开始),使用CFRunLoopAddObserver
增加主线程的runLoop,假如处于闲暇状况,则回调给函数MyOperationRunLoopObserverWaitingCallBack
,这个函数会履行行列的executeOperation
函数,executeOperation
函数会去判断当时queue是什么状况,假如是开始状况(start)或许上个使命的完结状况(lastExecuteFinish),则会去取履行行列的下一个使命,然后把行列的状况设置为履行中(Executing),然后回调出去,履行下一个使命,当使命完结,就会调用onOperationFinish
函数,把行列queue的状况设置成上个使命的完结状况(lastExecuteFinish),然后移除行列里现已完结的上一个使命,这时候等待下一次主线程再次进入beforeWaiting状况,回调给函数MyOperationRunLoopObserverWaitingCallBack
,重复刚刚那个进程。
需求留意的有
1.履行到onOperationFinish
函数时,移除完行列里现已完结的上一个使命后,假如行列的一切使命都完结了,就会置成一切使命都完结的状况(AllFinish),这时除非有新的使命参加,把行列状况设置成开始状况(Start),否则每次进入executeOperation
函数都会return掉
2.为了防止短时间内增加很多使命,我在executeOperation
函数增加了30ms的约束,防止使命履行过分频繁,这个能够依据实际情况修正
三.结束
闲暇行列有时候也能用于一些低优先级的UI改写操作,熟练使用它,说不定你比我更会发挥它的优点,尽量减少啥操作都扔子线程就觉得万事大吉的心理吧,并且手艺不嫌多,装装逼也行=。=