IOS开发 - 多线程 NSOperation

1,NSOperation使用

1)NSOperation简介

NSOperation是Cocoa中的一个抽象类,用来封装单个任务和代码执行一项操作,由于是抽象类,所以不能直接实例化使用,必须定义子类继承该抽象类来实现,比较常用的NSOperation的子类有NSInvocationOperation,另外,也可以自己继承NSOperation来实现线程的操作。
另外会使用到操作队列NSOperationQueue,它相当于一个线程队列或者可以叫做线程池,可以顺序执行队列中的操作,也可以设置队列中操作的优先级。

NSOperation有两个子类,NSInvocationOperation 和 NSBlockOperation。NSOperation也是设计用来扩展的,只需继承重写NSOperation的一个方法main。


2)NSOperation和GCD

/**
 GCD --> IOS4.0
 -GCD --> iOS 4.0
 - 将任务(block)添加到队列(串行/并发(全局)),指定 执行任务的方法(同步(阻塞)/异步)
 - 拿到 dispatch_get_main_queue()。 线程间通信
 - NSOperation无法做到,一次性执行,延迟执行,调度组(op相对复杂)
 
 NSOperation ----> iOS 2.0 (后来苹果改造了NSOperation的底层)
 - 将操作(异步执行)添加到队列(并发/全局)
 - [NSOperationQueue mainQueue] 主队列。 任务添加到主队列, 就会在主线程执行
 - 提供了一些GCD不好实现的,”最大并发数“
 - 暂停/继续 --- 挂起
 - 取消所有的任务
 - 依赖关系
 */

/**
 小结一下:
 只要是NSOperation的子类 就能添加到操作队列
 - 一旦操作添加到队列, 就会自动异步执行
 - 如果没有添加到队列, 而是使用start方法,会在当前线程执行操作
 - 如果要做线程间通信,可以使用[NSOperationQueue mainQueue]拿到主队列,往主队列添加操作(更新UI)
 */

3)NSOperation多线程Demo

基本用法

#pragma mark - 基本使用
// MARK : 线程间通信 最重要的代码
// 线程间的通信,代码实现,子线程中获取主线程,并在主线程中添加操作,可以嵌套
- (void)opDemo5
{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    [queue addOperationWithBlock:^{
        NSLog(@"耗时操作");
        [NSThread sleepForTimeInterval:2];
        
        //去主线程更新UI
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            NSLog(@"更新UI");
        }];
    }];
}


//MARk : NSBlockOperation使用
// 如下所示,NSBlockOperation,NSInvocationOperation两种对象可以同时放到一个队列中执行的,就是说是可以混合使用的
- (void)opDemo4
{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    for (int i = 0; i < 10; i++) {
        //直接使用队列的方法将操作添加到队列中去
        [queue addOperationWithBlock:^{
            [NSThread sleepForTimeInterval:1];
            NSLog(@"%@", [NSThread currentThread]);
            
        }];
    }
    
    //创建一个NSBlockOperation操作对象
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"op1-----%@", [NSThread currentThread]);
        
    }];
    //他们两个的执行顺序是不确定的
    [op1 addExecutionBlock:^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"op1---op1");
    }];
    
    [queue addOperation:op1];
    
    //创建并添加 NSInvocationOperation
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@"abc"];
    [queue addOperation:op2];
    
    
}


//MARK : NSBlockOperation使用
- (void)opDemo3
{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    //获取主队列, 和GCD中的主队列一个道理
    NSOperationQueue *q = [NSOperationQueue mainQueue];
    
    for (int i = 0; i < 10; i++) {
        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
            [NSThread sleepForTimeInterval:1];
            NSLog(@"%@", [NSThread currentThread]);
        }];
        [queue addOperation:op];
    }
    
    NSLog(@"done");
}

//MARK : 多个 NSInvocationOperation 使用
- (void)opDemo2
{
    //队列,在GCD中并发(全局)队列使用的最多。所以NSoperation技术直接把GCD里面的并发队列封装起来
    //NSOperationQueue队列,本质就是GCD里面的并发队列
    //操作就是GCD里面异步执行任务
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    //把多个操作放到队列中去
    for(int i = 0; i < 10; i++)
    {
        NSOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@"sdsdsd"];
        [queue addOperation:op];
    }
    
}

// MARK : 单个 NSInvocationOperation 的使用
- (void)opDemo1
{
    //1,创建操作
    NSOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@"downloadImage"];
    
    //2,启动 使用这个方法时候,会直接在当前线程中执行任务,达不到多线程的目的
//    [op start];
    
    //3,只要把操作添加到队列中, 就会自动的异步执行
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:op];
    
    NSLog(@"111%s", __func__);
}

- (void)downloadImage:(NSString *)picName
{
//    NSLog(@"%s", __func__);
    [NSThread sleepForTimeInterval:1];
    NSLog(@"%@ ----- %@", [NSThread currentThread], picName);
}
高级用法
#pragma mark - 高级操作
// MARk : 依赖关系
- (void)dependecy
{
    /*
     例如:
     1,登陆账号
     2,下载一张图片,
     3,图片更新到UI上
     
     这三个操作是有先后顺序的, 为了处理并发中的这种先后顺序,需要使用依赖关系
     */
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1,登陆账号 %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2,下载一张图片 %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"3,更绣UI %@", [NSThread currentThread]);
    }];
    
    [op2 addDependency:op1];
    [op3 addDependency:op2];
    
    //如果出现循环依赖,程序就会死锁
//    [op3 addDependency:op1];
    
    //watiUntilFinished 类似GCD调度组的通知
    //NO 不等待,会直接执行
    //YES 等待上面的操作执行结束,再 执行
    [self.queue addOperations:@[op1, op2] waitUntilFinished:YES];
    NSLog(@"test");
    
    //在主线程中更新UI
    [[NSOperationQueue mainQueue] addOperation:op3];
    NSLog(@"here");
    
    
}


// MARK : 取消队列中所有的操作
// 取消操作并不会影响队列的挂起状态
- (IBAction)cancleAll:(id)sender{
    //取消队列中所有操作
    [self.queue cancelAllOperations];
    
    NSLog(@"取消当前队列中所有的操作");
    
    //取消队列的挂起状态
    self.queue.suspended = NO;
}


// MARK : 暂停/继续 对队列中任务的暂停和继续
- (IBAction)pause:(id)sender
{
    //判断操作的数量,看队列中是不是有操作
    if (self.queue.operationCount == 0) {
        NSLog(@"没有操作");
        return;
    }
    
    // 暂停/继续
    self.queue.suspended = !self.queue.suspended;
    
    // 队列挂起以后队列中的任务还是有的
    if (self.queue.suspended) {
        NSLog(@"暂停");
    }else{
        NSLog(@"继续");
    }
}


// MARk : 最大并发数
- (void)opDemo6
{
    //设置最大并发数,并不是线程的数量
    // 这里设置最大并发数量,设置的是最多同时有多少个线程在执行,这里有个线程池的概念,当程序需要线程的时候,就会从线程池中取出一个线程,线程使用完毕时候还会放回线程池中去,在需要的识货再去线程池中取,线程使用的过程中还有个回收的过程
    self.queue.maxConcurrentOperationCount = 3;
    
    for (int i = 0; i < 20; i++) {
        NSOperation *op = [NSBlockOperation blockOperationWithBlock:^{
            
            [NSThread sleepForTimeInterval:1.0];
            
            NSLog(@"%@---%d", [NSThread currentThread], i);
        }];
        
        [self.queue addOperation:op];
    }
    
}


2,NSOperation

<pre name="code" class="objc">@interface NSOperation : NSObject {
@private
    id _private;
    int32_t _private1;
#if __LP64__
    int32_t _private1b;
#endif
}
//1,通过这个方法可以启动一个操作,但是操作只会在当前的线程中执行,相当于是同步执行
- (void)start;
- (void)main;
//2,取消当前的操作
@property (readonly, getter=isCancelled) BOOL cancelled;
- (void)cancel;

@property (readonly, getter=isExecuting) BOOL executing; //判断当前操作是否正在执行
@property (readonly, getter=isFinished) BOOL finished; //判断当前操作是否执行完成
@property (readonly, getter=isConcurrent) BOOL concurrent; // To be deprecated; use and override 'asynchronous' below
@property (readonly, getter=isAsynchronous) BOOL asynchronous NS_AVAILABLE(10_8, 7_0);
@property (readonly, getter=isReady) BOOL ready;

//3,添加依赖关系 和 移除依赖关系
- (void)addDependency:(NSOperation *)op;
- (void)removeDependency:(NSOperation *)op;
//4,获取当前操作所有的依赖关系
@property (readonly, copy) NSArray *dependencies;

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

//5,设置当前操作的优先级
@property NSOperationQueuePriority queuePriority;
//6,设置当前操作完成之后要执行的代码
@property (copy) void (^completionBlock)(void) NS_AVAILABLE(10_6, 4_0);

//7,等待完成之后继续操作
- (void)waitUntilFinished NS_AVAILABLE(10_6, 4_0);

@property double threadPriority NS_DEPRECATED(10_6, 10_10, 4_0, 8_0);
@property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0);
@property (copy) NSString *name NS_AVAILABLE(10_10, 8_0);

@end


 

3,NSBlockOperation

@interface NSBlockOperation : NSOperation {
@private
    id _private2;
    void *_reserved2;
}
//1,类方法创建一个操作
+ (instancetype)blockOperationWithBlock:(void (^)(void))block;
//2,向这个操作中添加子操作,子操作和当前操作执行顺序是不确定的,子操作和当前操作并没有什么联系。
- (void)addExecutionBlock:(void (^)(void))block;
@property (readonly, copy) NSArray *executionBlocks;

@end

4,NSInvocationOperation

@interface NSInvocationOperation : NSOperation {
@private
    id _inv;
    id _exception;
    void *_reserved2;
}
//1,通过一个实例方法,创建一个操作,并制定操作要执行的方法,缺点是,必须指定一个要执行的方法,异步执行的代码块要放到一个新的方法中去执行。
- (instancetype)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
- (instancetype)initWithInvocation:(NSInvocation *)inv NS_DESIGNATED_INITIALIZER;

@property (readonly, retain) NSInvocation *invocation;

@property (readonly, retain) id result;

@end

5,NSOperationQueue

<pre name="code" class="objc">@interface NSOperationQueue : NSObject {
@private
    id _private;
    void *_reserved;
}
//1,向队列中添加操作
- (void)addOperation:(NSOperation *)op;
//2,使用数组的方式,向队列中添加操作,并设置是否对等待
- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait NS_AVAILABLE(10_6, 4_0);
//3,通过block代码块来向队列中添加操作,一种更快捷的方式,无需创建NSOperation对象
- (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);

//4,取出队列中的操作,只读
@property (readonly, copy) NSArray *operations;
//5,队列中的操作数(任务数)
@property (readonly) NSUInteger operationCount NS_AVAILABLE(10_6, 4_0);
//6,队列中最大并发数,最多能够同时执行多少个任务
@property NSInteger maxConcurrentOperationCount;

enum {
    NSOperationQueueDefaultMaxConcurrentOperationCount = -1
};
//7,挂起,将队列中的任务挂起,暂停任务,挂起之后,队列中的任务不再	取出执行,但是任务还存在在队列中
@property (getter=isSuspended) BOOL suspended;

@property (copy) NSString *name NS_AVAILABLE(10_6, 4_0);

@property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0);

@property (assign /* actually retain */) dispatch_queue_t underlyingQueue NS_AVAILABLE(10_10, 8_0);
//8,取消所有的操作
- (void)cancelAllOperations;
//9,相当于是GCD调度组中的通知,在队列中所有的任务都执行完成之后会调用这个方法
- (void)waitUntilAllOperationsAreFinished;

//10,获取当前所在队列,获取主队列
+ (NSOperationQueue *)currentQueue NS_AVAILABLE(10_6, 4_0);
+ (NSOperationQueue *)mainQueue NS_AVAILABLE(10_6, 4_0);

@end


 


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值