版本记录
版本号 | 时间 |
---|---|
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
,也包括NSBlockOperation
、NSInvocationOperation
、NSOperationQueue
三个相关的类和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』
后记
秋未完,待续~~~
网友评论