基于NSOperation的串行线程
背景介绍:在接入七牛SDK的时候,发现SDK没有批量上传图片的接口,业务又涉及到了上传进度统计,并且要求一次性的图片完整上传。
开始的时候打算用GCD,写着写着感觉扩展性不好,可读性不高,取消机制也不是很友好,线程不能暂停,于是改成了NSBlockOperation来实现
.h文件
/**
串行队列
@param dataList 数据源
@param opreationBlock 运行回调
@param cancelBlock 中断回调
@param completionBlock 完成回调
@return 线程队列
*/
+ (NSOperationQueue *)startOperationOnSerialQueue:(NSArray *)dataList
opreationBlock:(void(^)(id data, NSOperationQueue *queue, NSInteger index, double totalProgress))opreationBlock
cancelBlock:(BOOL(^)(id data, NSInteger index))cancelBlock
completionBlock:(void(^)(BOOL success))completionBlock;
介绍下h文件,这个类方法可以创建一个线程池,串行队列的顺序由dataList数组源中的元素顺序决定,提供三种不同时机的回调,根据实际情况使用,opreationBlock是主要回调,相当于thread的main。
.m文件
+ (NSOperationQueue *)startOperationOnSerialQueue:(NSArray *)dataList
opreationBlock:(void(^)(id data, NSOperationQueue *queue, NSInteger index, double totalProgress))opreationBlock
cancelBlock:(BOOL(^)(id data, NSInteger index))cancelBlock
completionBlock:(void(^)(BOOL success))completionBlock
{
// 创建一个线程队列,最大并发数为1
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 1;
// 根据数据源创建Operation
__block NSBlockOperation *lastBlockOperation;
[dataList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
__block NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
// 如果设置了中断回调,优先检测中断回调是否触发
if (cancelBlock) {
BOOL cancelFlag = cancelBlock(obj,idx);
//如果中断了
if (cancelFlag) {
//取消所有线程
[queue cancelAllOperations];
// 触发一次完成回调
[queue addOperation:[NSBlockOperation blockOperationWithBlock:^{
if (completionBlock) {
completionBlock(NO);
}
}]];
} else {
if (opreationBlock) {
opreationBlock(obj, queue, idx, idx*1.0f/dataList.count);
}
}
} else {
if (opreationBlock) {
opreationBlock(obj, queue, idx, idx*1.0f/dataList.count);
}
}
}];
// 将当前线程设置为上一次线程的依赖,保证运行顺序
if (lastBlockOperation) {
[blockOperation addDependency:lastBlockOperation];
}
lastBlockOperation = blockOperation;
[queue addOperation:blockOperation];
if (idx == dataList.count-1) {
//当最后一个线程运行完毕后,触发完成回调
NSBlockOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
if (completionBlock) {
completionBlock(YES);
}
}];
[completionOperation addDependency:blockOperation];
[queue addOperation:completionOperation];
}
}];
return queue;
}
流程都有注释,主要逻辑就是将线程设置成单项依赖,然后在适当的时机执行中断判断,触发完成回调。到现在为止完成了同步操作的串行队列执行,但大家都知道,同步操作即使不用队列,也是串行执行的,所以这里要解决异步操作的串行队列执行,这就用到了NSOperationQueue的suspended功能,在执行异步操作前先暂停队列,等异步操作完成后,再继续队列。
具体实现参考
NSMutableArray *imageKeys = [NSMutableArray array];
__block NSString *lastResultKey = nil;
[GinOperationQueueManager startOperationOnSerialQueue:imageList opreationBlock:^(id data, NSOperationQueue *queue, NSInteger index, double totalProgress) {
// 这里挂起队列
queue.suspended = YES;
[self uploadSingleImage:data filename:[CEQiNiuManager createJPGImageKey] progress:nil completion:^(QNResponseInfo *info, NSString *key, NSDictionary *resp, UIImage *image) {
lastResultKey = key;
if (!key) {
if (completion) {
completion(NO, nil);
}
} else {
if (progress) {
progress(key, totalProgress ,index);
}
[imageKeys addObject:key];
}
//执行完成后继续队列
queue.suspended = NO;
}];
} cancelBlock:^BOOL(id data, NSInteger index) {
return ![lastResultKey isNotBlank] && index != 0;
} completionBlock:^(BOOL success) {
if (completion) {
completion(success, imageKeys.count!=0?imageKeys:nil);
}
}];
这里是源文件,供大家参考(https://github.com/ginhoor/GinOperationQueueManager)