NSOperation和GCD一樣,不用我們管理線程的生命周期,加鎖等問題,只要把操作封裝進NSOperation中,系統會自動幫我們創建線程,執行操作。而且他是面向對象的,我們看起來更容易理解,使用起來也更靈活。GCD提供的API都是C語言的,看起來確實有點頭痛。 NSOperation是一個抽象 ...
NSOperation和GCD一樣,不用我們管理線程的生命周期,加鎖等問題,只要把操作封裝進NSOperation中,系統會自動幫我們創建線程,執行操作。而且他是面向對象的,我們看起來更容易理解,使用起來也更靈活。GCD提供的API都是C語言的,看起來確實有點頭痛。
NSOperation是一個抽象類,我們得使用他的兩個子類NSInvocationOperation和NSBlockOperatio才能實現多線程,當然我們也可以自定義。那下麵就先介紹一下該怎麼使用。
NSInvocationOperation
代碼
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化一個對象,並把操作(相當於GCD里的任務)封裝進去
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
// 開始執行 一開始就執行run方法
[operation start];
NSLog(@"怎麼才執行我");
}
- (void)run {
[NSThread sleepForTimeInterval:1.0];
NSLog(@"執行操作%@",[NSThread currentThread]);
}
日誌
2016-11-06 08:23:38.999 TTTTTTTTTT[2872:38936] 執行操作<NSThread: 0x7a710ca0>{number = 1, name = main}
2016-11-06 08:23:38.999 TTTTTTTTTT[2872:38936] 怎麼才執行我
分析:
(1)操作預設在主線程中執行,看列印的第一條日誌。
(2)是同步執行的,會堵塞當前線程,看第二條日誌,run方法執行完,“怎麼才執行我”才列印。
NSBlockOperation
代碼
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化,並把要執行的操作封裝進block中
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"執行操作%@",[NSThread currentThread]);
}];
[operation start];
NSLog(@"我知道我想早點執行,那是不可能的");
}
日誌
2016-11-06 08:39:16.815 TTTTTTTTTT[3528:48184] 執行操作<NSThread: 0x60800006e4c0>{number = 1, name = main}
2016-11-06 08:39:16.816 TTTTTTTTTT[3528:48184] 我知道我想早點執行,那是不可能的
分析:是不是看起來和NSInvocationOperation一樣,都是同步在主線程中執行,其實NSBlockOperation是可以併發執行的;
NSBlockOperation的併發執行
代碼
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化,並把要執行的操作封裝進block中
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
// 添加任務A
NSLog(@"執行操作A%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
// 添加任務B
NSLog(@"執行操作B%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
// 添加任務C
NSLog(@"執行操作C%@",[NSThread currentThread]);
}];
[operation start];
}
日誌
2016-11-06 08:43:48.102 TTTTTTTTTT[3707:50733] 執行操作B<NSThread: 0x60000007f200>{number = 3, name = (null)}
2016-11-06 08:43:48.102 TTTTTTTTTT[3707:50702] 執行操作A<NSThread: 0x60000007cac0>{number = 1, name = main}
2016-11-06 08:43:48.102 TTTTTTTTTT[3707:50734] 執行操作C<NSThread: 0x608000268840>{number = 4, name = (null)}
分析:
(1)看到沒有,操作B和C都是在子線程執行的,實現了非同步併發。
(2)當NSBlockOperation里的要執行的操作的數量 >1的時,就會非同步併發執行;否則(當NSBlockOperation里的要執行的操作只有一個),就會預設在主線程中同步執行。
NSOperationQueue
NSInvocationOperation裡面的操作也想非同步併發執行可以不可以呢?當然沒問題了,用NSOperationQueue 就行。
代碼
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化一個NSOperationQueue對象
NSOperationQueue *queue= [NSOperationQueue new];
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run1) object:nil];
// 添加操作
[queue addOperation:operation1];
NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run2) object:nil];
// 添加操作
[queue addOperation:operation2];
// 還可以這樣添加操作
[queue addOperationWithBlock:^{
NSLog(@"執行第3個操作%@",[NSThread currentThread]);
}];
}
- (void)run1 {
NSLog(@"執行第1個操作%@",[NSThread currentThread]);
}
- (void)run2 {
NSLog(@"執行第2個操作%@",[NSThread currentThread]);
}
日誌
2016-11-06 08:57:03.498 TTTTTTTTTT[4195:57383] 執行第2個操作<NSThread: 0x60800006f8c0>{number = 3, name = (null)}
2016-11-06 08:57:03.498 TTTTTTTTTT[4195:57384] 執行第1個操作<NSThread: 0x608000079480>{number = 4, name = (null)}
2016-11-06 08:57:03.498 TTTTTTTTTT[4195:57386] 執行第3個操作<NSThread: 0x600000079e40>{number = 5, name = (null)}
分析:
(1)三個操作分別在不同的線程中執行,實現了併發。
(2)添加進隊列的任務會自動執行,不要我們開啟了。
[operation start];
不用寫了。
(3)添加進隊列的任務也是遵循先進先出的FIFO準則。那有人就要問了,為什麼第一個操作不是第一個先執行完?這其實和100米賽跑一個起跑的不是第一個到終點一個道理,不矛盾。
總結
NSOperation的使用步驟和GCD沒啥區別,都是確定要執行的操作,把操作放進NSOperation中,開始執行。如果想非同步併發操作,在加一步,把NSOperation放進隊列中。記住一點不能直接使用NSOperation,要使用它的兩個子類NSInvocationOperation和NSBlockOperatio。
其實關於NSOperation還有很多基本的屬性和方法,下一篇文章再講。看到NSOperation的使用是不是舒服多了……