iOS:NSOperation基本使用

NSOperation

iOS4推出GCD后,NSOPeration和NSOperationQueue是基于GCD更高一层的封装。所以NSOperation完全地面向对象。比GCD更简单易用、代码可读性也更高。

NSOperation和NSOperationQueue分别对应GCD的任务和队列,NSOperation对比GCD会带来一点额外的系统开销,但是Operation操作性更好

NSOperation作为一个基类,不能直接创建任务,需要通过他的子类NSInvocationOperationNSBlockOperation创建任务;而NSOperationQueue是用来管理任务的队列

@interface NSOperation : NSObject
@interface NSInvocationOperation : NSOperation 
@interface NSBlockOperation : NSOperation 

//管理任务的队列
@interface NSOperationQueue : NSObject 

还可以通过KVO监听操作对象的各种状态,还可以添加依赖,设置优先级等等

@property (readonly, getter=isCancelled) BOOL cancelled;
@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
@property (readonly, getter=isReady) BOOL ready; //任务想要执行ready状态必须为YES

设置优先级: 异步执行的时候,优先级高的先执行
添加依赖: addDependency: B依赖A,需要等到A执行完了,B进入ready状态,再执行B,

如下代码:[bo addDependency:ao]; bo 依赖于ao,如果不添加到队列,那么需要自己手动start,并且 ao要先 start,如果bo先start那么,会崩溃,因为bo没有进入ready状态就开始start

- (void)test {
  NSInvocationOperation *ao = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(testio:) object:@{@"key":@"value"}];
    
    NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"bb---%@",[NSThread currentThread]);
    }];
    [bo addDependency:ao];
    [ao start];
    [bo start];
}
1:NSInvocationOperation

NSInvocationOperation没有额外添加任务的方法,使用NSInvocationOperation创建的对象只会有一个任务。而且start方法是在当前线程中执行,阻塞当前线程,只有等到任务结束后,才会往下执行

NSInvocationOperation *io = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(invocationOperation) object:nil];
io.queuePriority = NSOperationQueuePriorityVeryLow;
[io start];
2:NSBlockOperation

NSBlockOperation通过blockOperationWithBlock:创建对象,在创建的时候也添加一个任务。如果想添加更多的任务,可以使用addExecutionBlock:方法。也可以通过init创建

    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1111-%@",[NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
        NSLog(@"3333-%@",[NSThread currentThread]);
    }];
    [blockOperation start];

注意:
当只有一个任务时,NSBlockOperation不会开辟新线程,直接在当前线程同步执行任务。并且 阻塞当前线程

任务数大于1时,第一个任务,不会开辟线程,直接在当前线程同步执行任务(同上)。而通过addExecutionBlock:方法添加的其他任务会开辟新线程,异步执行任务。(前提是没有添加到NSOperationQueue中,添加到NSOperationQueue的任务默认都是异步执行,全部开启新的子线程)

二、NSOperationQueue

NSOperationQueue:相当于CGD中的队列,而且是并行队列

    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    [queue addOperationWithBlock:^{
        NSLog(@"1111-%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"2222-%@",[NSThread currentThread]);
    }];

可以通过[queue addOperations:@[operation0,operation1] waitUntilFinished:YES];达到异步下载完成后,再执行剩下的某个任务,相当于CGD中的dispatch_barrier_async方法。

NSOperationQueue的属性控制
  1. setMaxConcurrentOperationCount:方法可以设置队列的最大并发数
  2. cancel方法取消单个操作对象。cancelAllOperations方法取消队列中全部操作(针对未执行的任务),当我们调用cancel方法的时候,他只是将isCancelled设置为YES
  3. 暂停suspended:当任务的suspended设置为YES,队列会停止对任务调度(针对未执行的任务)。如果任务正在执行将不会受到影响,因为任务已经被队列调度到一个线程上并执行。

总结:GCD的核心是C语言写的系统服务,执行和操作简单高效,因此NSOperation底层也通过GCD实现,换个说法就是NSOperation是对GCD更高层次的抽象,这是他们之间最本质的区别

所以NSOperation是对GCD的区别如下:

  1. 依赖关系,NSOperation可以设置两个NSOperation之间的依赖,第二个任务依赖于第一个任务完成执行,GCD无法设置依赖关系,不过可以通过dispatch_barrier_async来实现这种效果;

  2. KVO(键值对观察),NSOperation和容易判断Operation当前的状态(是否执行,是否取消),对此GCD无法通过KVO进行判断;

  3. 优先级,NSOperation可以设置自身的优先级,但是优先级高的不一定先执行,GCD只能设置队列的优先级,无法在执行的block设置优先级;

  4. 继承,NSOperation是一个抽象类实际开发中常用的两个类是NSInvocationOperation和NSBlockOperation,同样我们可以自定义NSOperation,GCD执行任务可以自由组装,没有继承那么高的代码复用度;

  5. 效率,直接使用GCD效率确实会更高效,NSOperation会多一点开销,但是通过NSOperation可以获得依赖,优先级,继承,键值对观察这些优势,相对于多的那么一点开销确实很划算

NSOperation和NSOperationQueue的注意点
  1. 一个NSOperation的任务执行了start后,不能添加到NSOperationQueue,因为他已经执行了,状态已经finished。还要添加到队列会crash,-[NSOperationQueue addOperation:]: operation is finished and cannot be enqueued'
  2. 添加依赖:[bo addDependency:ao]; bo 依赖于ao,如果不添加到队列,那么需要自己手动start,并且 ao要先 start,如果bo先start那么,会崩溃,因为bo没有进入ready状态就开始start,如果添加到队列就不用我们手动start,不用管,队列会去调度

三、多线程间的通信

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    
{   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 1.子线程下载图片
   		//code ...
        
        // 2.回到主线程设置图片
        dispatch_async(dispatch_get_main_queue(), ^{
            //code ...
        });
    });
}

四、dispatch也是可以cancel的

已经执行的任务不能cancel。
只能取消未执行的任务,dispatch_block_cancel()dispatch_source_cancel(),就能取消未执行的任务。注意 取消的必须是dispatch_block_t类型,(就是通过dispatch_block_create创建的任务),下面的b1就不能放在dispatch_block_cancel(b1);里面,放进去就crash,因为b1不是dispatch_block_create创建的。

dispatch_block_t
dispatch_block_create(dispatch_block_flags_t flags, dispatch_block_t block); //flags 标记,后面某个时机使用
- (void)viewDidLoad {
    dispatch_block_t b1 = ^{
        for (int i = 0; i < 5; i ++) {
            sleep(1);
            NSLog(@"bb11---%@",[NSThread currentThread]);
        }
    };
    dispatch_block_t b2 = dispatch_block_create(0, ^{
        for (int i = 0; i < 5; i ++) {
            NSLog(@"bb22---%@",[NSThread currentThread]);
        }
    });
    
//    dispatch_block_t b2 = ^{};
    dispatch_queue_t qu = dispatch_queue_create("com.ob", DISPATCH_QUEUE_SERIAL);
    dispatch_async(qu,b1);
    dispatch_async(qu,b2);

    dispatch_block_cancel(b2);
    dispatch_source_cancel(dispatch_source_t  _Nonnull source)
}
dispatch_get_global_queue

dispatch_get_global_queue 会获取一个全局队列,我们姑且理解为系统为我们开启的一些全局线程。我们用priority指定队列的优先级,而flag作为保留字段备用(一般为0)。
全局队列可以设置优先级,但是任务不能设置优先级

 dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 dispatch_queue_t aHQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
 dispatch_queue_t aLQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);  

五、异步转同步

异步转同步:本质就是让同步任务等待异步执行完毕,再回到同步任务,所以有很多实现:dispatch_group_tdispatch_semaphore_t和锁lock都可以实现,

- (id)sync_to_async:(NSString *)obj {
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("ob", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"一个同步任务开始");
    __block id value;
    NSLock *lock = [[NSLock alloc] init];
      [lock lock];
//    dispatch_group_enter(group);
    dispatch_async(queue, ^{
      
        sleep(5);
        value = @YES;
        NSLog(@"执行异步任务 %@", @YES);
//        dispatch_group_leave(group);
        [lock unlock];
    });
    [lock lock];
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"等待异步执行完,回到同步任务,其实就是在这里等待: %@", value);
    [lock unlock];
    return value;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值