美文网首页iOS线程
ios并发机制(二) —— NSOperation实现多并发之创

ios并发机制(二) —— NSOperation实现多并发之创

作者: 刀客传奇 | 来源:发表于2017-08-16 16:55 被阅读24次

版本记录

版本号 时间
V1.0 2017.08.15

前言

信号量机制是多线程通信中的比较重要的一部分,对于NSOperation可以设置并发数,但是对于GCD就不能设置并发数了,那么就只能靠信号量机制了。接下来这几篇就会详细的说一下并发机制。感兴趣的可以看这几篇文章。
1. ios并发机制(一) —— GCD中的信号量及几个重要函数

先认识一下NSOperation

NSOperation是苹果提供给我们的一套多线程解决方案。实际上NSOperation是基于GCD更高一层的封装,但是比GCD更简单易用、代码可读性也更高。

NSOperation并没有开辟线程的能力,在多线程并发中需要NSOperationQueue的配合,才能实现异步并发操作。


NSOperation相关API

下面我们就看一下NSOperation的相关API。

@interface NSOperation : NSObject {
@private
    id _private;
    int32_t _private1;
#if __LP64__
    int32_t _private1b;
#endif
}

- (void)start;
- (void)main;

@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;

- (void)addDependency:(NSOperation *)op;
- (void)removeDependency:(NSOperation *)op;

@property (readonly, copy) NSArray<NSOperation *> *dependencies;

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

@property NSOperationQueuePriority queuePriority;

@property (nullable, copy) void (^completionBlock)(void) NS_AVAILABLE(10_6, 4_0);

- (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 (nullable, copy) NSString *name NS_AVAILABLE(10_10, 8_0);

@end



NS_CLASS_AVAILABLE(10_6, 4_0)
@interface NSBlockOperation : NSOperation {
@private
    id _private2;
    void *_reserved2;
}

+ (instancetype)blockOperationWithBlock:(void (^)(void))block;

- (void)addExecutionBlock:(void (^)(void))block;
@property (readonly, copy) NSArray<void (^)(void)> *executionBlocks;

@end


NS_CLASS_AVAILABLE(10_5, 2_0)
NS_SWIFT_UNAVAILABLE("NSInvocation and related APIs not available")
@interface NSInvocationOperation : NSOperation {
@private
    id _inv;
    id _exception;
    void *_reserved2;
}

- (nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg;
- (instancetype)initWithInvocation:(NSInvocation *)inv NS_DESIGNATED_INITIALIZER;

@property (readonly, retain) NSInvocation *invocation;

@property (nullable, readonly, retain) id result;

@end

FOUNDATION_EXPORT NSExceptionName const NSInvocationOperationVoidResultException NS_AVAILABLE(10_5, 2_0);
FOUNDATION_EXPORT NSExceptionName const NSInvocationOperationCancelledException NS_AVAILABLE(10_5, 2_0);

static const NSInteger NSOperationQueueDefaultMaxConcurrentOperationCount = -1;

NS_CLASS_AVAILABLE(10_5, 2_0)
@interface NSOperationQueue : NSObject {
@private
    id _private;
    void *_reserved;
}

- (void)addOperation:(NSOperation *)op;
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait NS_AVAILABLE(10_6, 4_0);

- (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);

@property (readonly, copy) NSArray<__kindof NSOperation *> *operations;
@property (readonly) NSUInteger operationCount NS_AVAILABLE(10_6, 4_0);

@property NSInteger maxConcurrentOperationCount;

@property (getter=isSuspended) BOOL suspended;

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

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

@property (nullable, assign /* actually retain */) dispatch_queue_t underlyingQueue NS_AVAILABLE(10_10, 8_0);

- (void)cancelAllOperations;

- (void)waitUntilAllOperationsAreFinished;

#if FOUNDATION_SWIFT_SDK_EPOCH_AT_LEAST(8)
@property (class, readonly, strong, nullable) NSOperationQueue *currentQueue NS_AVAILABLE(10_6, 4_0);
@property (class, readonly, strong) NSOperationQueue *mainQueue NS_AVAILABLE(10_6, 4_0);
#endif

@end

NS_ASSUME_NONNULL_END

大家可以看到,上面的API中包括NSOperation,也包括NSBlockOperationNSInvocationOperationNSOperationQueue三个相关的类和API接口。


NSOperation多线程实现步骤

NSOperation相当于GCD中的任务,NSOperationQueue相当于GCD中的队列,其实现多线程并发主要有以下几个步骤:

  • 创建任务:注意就是将任务封装在NSOperation子类的对象中。
  • 创建队列:创建NSOperationQueue对象。
  • 加入任务到队列:将任务NSOperation对象,添加到NSOperationQueue对象中。

下面我们就详细的说一下这几个步骤。

1. 创建任务

这里需要说明一下,NSOperation只是一个抽象类,不能用它直接创建任务,我们一般都是用其子类创建任务,它的子类分为三种:

  • NSInvocationOperation
  • NSBlockOperation
  • 自定义的NSOperation子类

下面我们就看一下具体创建任务的情况。

使用子类NSInvocationOperation创建任务

先看代码。

#import "JJInvocationOperationVC.h"

@interface JJInvocationOperationVC ()

@end

@implementation JJInvocationOperationVC

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperationDidInstance) object:nil];
    
    //调用start方法开始
    [operation start];
}

#pragma mark - Action && Notification

- (void)invocationOperationDidInstance
{
    NSLog(@"NSInvocationOperation");
    NSLog(@"thread = %@",[NSThread currentThread]);
}

@end

下面看输出结果

2017-08-16 16:30:30.062453+0800 JJOC[10120:4497369] NSInvocationOperation
2017-08-16 16:30:30.062568+0800 JJOC[10120:4497369] thread = <NSThread: 0x170072e00>{number = 1, name = main}

从上面可以看出来,只是创建了一个任务,还是在主线程执行的,并没有开启新的线程。

使用子类NSBlockOperation创建任务

还是直接看代码。

#import "JJBlockOperationVC.h"

@interface JJBlockOperationVC ()

@end

@implementation JJBlockOperationVC

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"NSBlockOperation");
        NSLog(@"thread = %@",[NSThread currentThread]);
    }];
    
    //调用start方法开始
    [operation start];
}

@end

下面看输出结果

2017-08-16 16:35:06.855774+0800 JJOC[10123:4499602] NSBlockOperation
2017-08-16 16:35:06.855873+0800 JJOC[10123:4499602] thread = <NSThread: 0x170071000>{number = 1, name = main}

从上面可以看出来,只是创建了一个任务,还是在主线程执行的,并没有开启新的线程。

注意:NSBlockOperation 还有一个方法addExecutionBlock,这个方法可以在主线程或者其他线程执行,也可以这么理解这个方法可能开启线程也可能不开启新的线程只在主线程执行,下面我们看一下代码。

#import "JJBlockOperationVC.h"

@interface JJBlockOperationVC ()

@end

@implementation JJBlockOperationVC

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"NSBlockOperation");
        NSLog(@"thread = %@",[NSThread currentThread]);

    }];
    
    [operation addExecutionBlock:^{
        NSLog(@"thread_1 = %@",[NSThread currentThread]);
    }];
    
    [operation addExecutionBlock:^{
        NSLog(@"thread_2 = %@",[NSThread currentThread]);
    }];
    
    [operation addExecutionBlock:^{
        NSLog(@"thread_3 = %@",[NSThread currentThread]);
    }];
    
    //调用start方法开始
    [operation start];
}

@end

下面看输出结果

2017-08-16 16:38:20.070254+0800 JJOC[10126:4500144] NSBlockOperation
2017-08-16 16:38:20.070436+0800 JJOC[10126:4500144] thread = <NSThread: 0x17007bf00>{number = 1, name = main}
2017-08-16 16:38:20.070446+0800 JJOC[10126:4500181] thread_1 = <NSThread: 0x174264f40>{number = 3, name = (null)}
2017-08-16 16:38:20.070501+0800 JJOC[10126:4500144] thread_2 = <NSThread: 0x17007bf00>{number = 1, name = main}
2017-08-16 16:38:20.070509+0800 JJOC[10126:4500181] thread_3 = <NSThread: 0x174264f40>{number = 3, name = (null)}

大家可以发现,开启了一个number = 3的线程,thread_2并没有开启新线程,仍在主线程执行。

使用自定义的NSOperation子类创建任务

下面还是直接看代码。

1. JJCustomOperationVC.h
#import <UIKit/UIKit.h>

@interface JJCustomOperationVC : UIViewController

@end
2. JJCustomOperationVC.m
#import "JJCustomOperationVC.h"
#import "JJOperation.h"

@interface JJCustomOperationVC ()

@end

@implementation JJCustomOperationVC

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    JJOperation *operation = [[JJOperation alloc] init];
    
    [operation start];
}

@end
3. JJOperation.h
#import <Foundation/Foundation.h>

@interface JJOperation : NSOperation

@end
4. JJOperation.m
#import "JJOperation.h"

@implementation JJOperation

#pragma mark - Override Base Function

//重写main方法,里面存放需要执行的任务

- (void)main
{
    NSLog(@"自定义的Operation");
    NSLog(@"thread = %@",[NSThread currentThread]);
}

@end

下面我们看输出结果

2017-08-16 16:51:08.461304+0800 JJOC[10130:4502372] 自定义的Operation
2017-08-16 16:51:08.461408+0800 JJOC[10130:4502372] thread = <NSThread: 0x17406c0c0>{number = 1, name = main}

从输出我们可以看到,还是没有开启新的线程。

参考文章

1. iOS多线程--彻底学会多线程之『NSOperation』
2. iOS多线程--彻底学会多线程之『pthread、NSThread』
3. iOS多线程--彻底学会多线程之『GCD』
4. iOS多线程--彻底学会多线程之『RunLoop』

后记

未完,待续~~~

相关文章

网友评论

    本文标题:ios并发机制(二) —— NSOperation实现多并发之创

    本文链接:https://www.haomeiwen.com/subject/gjbirxtx.html