面试题 For BearLiniOSios

NSOperation和NSOperationQueue的一些事

2015-11-02  本文已影响4212人  树下老男孩

在面对多线程的时候,大多数会选择NSOperation或者GCD来实现,GCD由于使用起来非常方便,应该是很多开发者的首选,不过你会发现其实很多开源代码都是使用NSOpertaion来执行异步任务,所以这次我们来说说NSOperation跟NSOperationQueue,以及它的强大之处。

NSOPerationQueue

NSOperation可以通过调用start方法同步地执行相应的任务,不过通常NSOprtaion都是配合NSOPerationQueue来使用的,NSOperationQueue可以看作是一种高级的dispatch queue,将NSOperation加入到queue中,queue会自动异步的执行该NSOperation

//创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];  

//将NSOperation加入队列之后,queue会自动执行该operation
[queue addOperation:operation];  

在gcd编程的时候,我们无法取消block对应的任务,不过NSOPerationQueue之所以称为高级的dispatch queue除了能异步的执行任务之外,还能够:

// 取消一个任务 
[operation cancel];  
  
// 取消queue中所有的任务  
[queue cancelAllOperations]; 
//设置并发数目为2
queue.maxConcurrentOperationCount = 2;  

假如maxConcurrentOperationCount的值设为1,可以看作该队列为串行队列,每次只能执行一个任务。不过NSOPerationQueue不是先进先出(FIFO)队列,这点跟dispatch queue有点区别,dispatch queue中的block会按照FIFO的顺序去执行,NSOPerationQueue会根据Operation的状态(是否Ready)以及优先级来确定执行的NSOperation的顺序。

// 暂停队列运行任务
[queue setSuspended:YES];

// 继续
[queue setSuspended:NO];

UserInteractive: 任务跟界面的一些UI相关,比如绘制屏幕内容跟处理点击事件等,处于最高优先级的任务

UserInitiated : 用户一些请求的任务,关系到后面的交互,比如用户点击消息按钮后获取邮件列表的任务

** Utility:** 处理一些用户并不立即需要结果的任务,比如定期的内容更新之类的任务

** Background**:后台任务,用户不会察觉到这些任务,比如后台对文件进行索引方便后续搜索,优先级最低;

** Default:** 默认值,介于UserInitiated跟Utility之间

NSOperaion

NSOperation可以看作是高级的dispatch block(dispatch_block_t),我们可以将一个任务封装成一个NSOperation对象,然后放到NSOperationQueue中去异步执行。NSOperation分为concurrent(并发任务)跟non-concurrent(非并发任务)两种,两者主要是生命周期的管理有些区别,NSOperation是一个抽象类,我们需要继承于它并实现一些方法。(当任务相对简单的时候,可以直接使用NSInvocationOperation或是NSBlockOperation,这两者也都继承自NSOperation)

@interface NonConcurrentOperation : NSOperation
@end
@implementation NonConcurrentOperation

-(void)main
{
    NSLog(@"main called");
    
    dispatch_time_t time=dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC);
    __weak typeof(self) weakSelf = self;
    dispatch_after(time, dispatch_get_main_queue(), ^{
        
        NSLog(@"weakSelf:%@",weakSelf);
    });
}
-(void)dealloc
{
    NSLog(@"dealloc called");
}

@end

non-concurrent operation会同步的运行main方法,不管期间的任务需要运行多长时间,当在执行完main方法后该operation对象会被释放。假如你在main方法中执行了异步的操作时会出现错误,比如上述的代码可以发现main方法运行完后就调用dealloc方法释放了,导致weakSelf的值为空。

NSOperations[1467:91483] main called
NSOperations[1467:91483] dealloc called
NSOperations[1467:91218] weakSelf:(null)
//状态枚举
typedef NS_ENUM(NSInteger, ConcurrentOperationState) {
    ConcurrentOperationReadyState = 1,
    ConcurrentOperationExecutingState,
    ConcurrentOperationFinishedState
};

@interface ConcurrentOperation ()
@property (nonatomic, assign) ConcurrentOperationState state;
@end

@implementation ConcurrentOperation

- (BOOL)isReady {
    self.state = ConcurrentOperationReadyState;
    return self.state == ConcurrentOperationReadyState;
}
- (BOOL)isExecuting{
    return self.state == ConcurrentOperationExecutingState;
}
- (BOOL)isFinished{
    return self.state == ConcurrentOperationFinishedState;
}

- (void)start {
    __weak typeof(self) weakSelf = self;
    dispatch_time_t time=dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC);
    dispatch_after(time, dispatch_get_main_queue(), ^{

        //kvo:结束
        [weakSelf willChangeValueForKey:@"isFinished"];
        weakSelf.state = ConcurrentOperationFinishedState;
        [weakSelf didChangeValueForKey:@"isFinished"];
        
        NSLog(@"finished :%@",weakSelf);
    });
    NSLog(@"start called");

    //kvo:正在执行
    [weakSelf willChangeValueForKey:@"isExecuting"];
    weakSelf.state = ConcurrentOperationExecutingState;
    [weakSelf didChangeValueForKey:@"isExecuting"];
}

-(void)dealloc{
    NSLog(@"dealloc called");
}

可以从打印的日志看到,当start方法结束后对象并没有立即被释放,只有发出isFinished的kvo通知后,该operation对象才会被释放

NSOperations[1556:103301] start called
NSOperations[1556:103242] finished :<ConcurrentOperation: 0x79a47bf0>
NSOperations[1556:103242] dealloc called
NSOperation状态

从上面的图可以看到nsoperation的几种状态,当这几个状态值改变时需要使用KVO通知,其中处于Pending、Ready跟Executing状态的operation是可以被cancel的,而当operation处于finished状态是无法被取消的。当operation成功结束、失败或者被取消了,isFinished的值都会被设置为yes,所以不能仅仅靠isFinished==YES认为operation成功执行。

typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};

//设置优先级
[operation1 setQueuePriority:NSOperationQueuePriorityVeryLow]; 

当operation被添加到队列之后,NSOperationQueue会浏览所有的operation,优先运行那些处于ready状态且优先级较高的操作。

    
@interface NonConcurrentOperation ()
@property(nonatomic,strong)NSNumber *number;
@end

@implementation NonConcurrentOperation

-(id)initWithNumber:(NSNumber *)number{
    self = [super init];
    if (self) {
        self.number = number;
    }
    return self;
}

-(void)main
{
    NSLog(@"main called, %@",self.number);
}
@end

//测试代码
NSOperationQueue  *queue1 = [NSOperationQueue new];
NSOperationQueue  *queue2 = [NSOperationQueue new];
    
NonConcurrentOperation *op1 = [[NonConcurrentOperation alloc] initWithNumber:@(1)];
NonConcurrentOperation *op2 = [[NonConcurrentOperation alloc] initWithNumber:@(2)];
NonConcurrentOperation *op3 = [[NonConcurrentOperation alloc] initWithNumber:@(3)];
NonConcurrentOperation *op4 = [[NonConcurrentOperation alloc] initWithNumber:@(4)];

//添加依赖
[op1 addDependency:op2];
[op2 addDependency:op3];

//可以依赖不同队列的operation
[op3 addDependency:op4];
    
[queue1 addOperation:op1];
[queue1 addOperation:op2];
[queue1 addOperation:op3];
[queue2 addOperation:op4]; //添加到不同队列中

输出结果:

NSOperations[2105:179596] main called, 4
NSOperations[2105:179596] main called, 3
NSOperations[2105:179596] main called, 2
NSOperations[2105:179596] main called, 1

添加依赖的时候需要注意不要相互依赖:

[op1 addDependency:op2];
[op2 addDependency:op1];

如果相互依赖,双方都会等待对方结束导致相互之间都无法执行。

互相依赖.png
//获取用户信息需要登陆完才能执行
[userInfoOperation addDependency:loginOperation];

//收藏需要获取到用户信息后才能执行
[favorOperation addDependency:userInfoOperation];
业务间的联系.png

不过NSOperation跟NSOperation底层也是基于GCD实现的,它是更高层次的抽象,当你框架设计涉及到这块内容的时候应该优先考虑使用NSOperations,这样框架的结构相对会好些,类似SDwebimgag跟AFNetworking等很多开源框架都在使用NSOperations。不过不可否认gcd代码编写更简便,这边就当抛砖引玉吧~

参考资料

NSOperation
認識NSOperation
Advanced NSOperations

上一篇下一篇

猜你喜欢

热点阅读