一粪滤、介紹
NSOperation是一個抽象類,可以使用系統(tǒng)提供的子類或者自己實現(xiàn)它的子類,來完成多線程隧熙。我們平時提到的NSOperation就默認包含了它的子類,具有以下特性:
1幻林、是OC語言中基于GCD的面向對象的封裝贞盯;
2、使用起來比GCD更加簡單(面向對象)滋将;
3邻悬、提供了一些用GCD不好實現(xiàn)的功能,比如可以取消在任務處理隊列中的任務随闽,添加任務間的依賴關系等等父丰;
4、蘋果推薦使用,使用NSOperation不用關心線程以及線程的生命周期掘宪;
5蛾扇、可以指定操作之間的依賴關系,是將操作添加到隊列魏滚。
6镀首、并發(fā)隊列,異步執(zhí)行(多個線程鼠次,無序執(zhí)行)更哄。
NSOperation方便統(tǒng)一管理,適用一些大型的復雜的場合腥寇,比如我們常用的網絡框架:AFNetworking成翩、SDWebImage等等。
二赦役、NSOperation 和 NSOperationQueue 實現(xiàn)多線程的步驟
1麻敌、先將需要執(zhí)行的操作封裝到一個 NSOperation 對象中
2、然后將 NSOperation 對象添加到 NSOperationQueue 中
3掂摔、系統(tǒng)會自動將 NSOperationQueue 中的 NSOperation 取出來
4术羔、講取出來的 NSOperation 封裝的操作放到一條新線程中執(zhí)行
三赢赊、NSOperation 的子類
NSOperation是一個抽象類,并不具備封裝操作的能力, 必須要使用它的子類
實現(xiàn)NSOperation子類的方式有3種:
1级历、NSInvocationOperation:較少使用释移;
2、NSBlockOperation:最常使用鱼喉;
3秀鞭、自定義子類繼承NSOperation,實現(xiàn)內部相應的方法:很少使用扛禽。
我們要做的就是將以上3種操作的任意一種添加到NSOperationQueue來使用锋边。
1、NSInvocationOperation
1.1编曼、直接執(zhí)行操作(同步)
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self invocationOperationTest];
}
//創(chuàng)建一個操作并執(zhí)行
- (void)invocationOperationTest {
// 創(chuàng)建操作(最后的object參數(shù)是傳遞給selector方法的參數(shù))
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(demo:) object:@"這是一個參數(shù)"];
[operation start];
}
// 將參數(shù)與當前線程打印
- (void)demo:(NSString *)str {
NSLog(@"%@--%@",str,[NSThread currentThread]);
}
/*************************執(zhí)行結果****************************/
2017-09-07 10:43:53.594 TextDemo[1074:374562] 這是一個參數(shù)--<NSThread: 0x6100000729c0>{number = 1, name = main}
創(chuàng)建初始化一個NSInvocationOperation對象豆巨,并且根據一個對象(self)
和selector
來創(chuàng)建操作,默認情況下掐场,調用了start
方法后并不會開一條新線程去執(zhí)行操作往扔,而是在當前線程同步執(zhí)行操作。
1.2熊户、將操作添加到NSOperationQueue執(zhí)行
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self invocationOperationTest];
}
// 將操作添加到隊列
- (void)invocationOperationTest {
// 創(chuàng)建操作(最后的object參數(shù)是傳遞給selector方法的參數(shù))
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(demo:) object:@"這是一個參數(shù)"];
// 創(chuàng)建操作隊列
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
// 將操作添加到操作隊列
[operationQueue addOperation:operation];
}
// 將參數(shù)與當前線程打印
- (void)demo:(NSString *)str {
NSLog(@"%@--%@",str,[NSThread currentThread]);
}
/*************************執(zhí)行結果****************************/
2017-09-07 10:51:13.312 TextDemo[1161:423494] 這是一個參數(shù)--<NSThread: 0x600000078740>{number = 4, name = (null)}
根據打印結果萍膛,可以看出新開啟了一個線程執(zhí)行操作,且是異步執(zhí)行的嚷堡。
2蝗罗、NSBlockOperation
2.1、執(zhí)行一個操作(同步)
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self blcokOperationText];
}
- (void)blcokOperationText
{
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1:%@", [NSThread currentThread]);
}];
[operation start];
}
/*************************執(zhí)行結果****************************/
2017-09-07 11:01:31.491 TextDemo[1258:484332] 1:<NSThread: 0x600000066b80>{number = 1, name = main}
可以看到這種方法非常簡單蝌戒,有點類似于GCD串塑,它也是同步執(zhí)行的。
2.2北苟、添加多個操作執(zhí)行(異步)
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self blcokOperationText];
}
- (void)blcokOperationText
{
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1:%@", [NSThread currentThread]);
}];
// 再添加3操作
[operation addExecutionBlock:^() {
NSLog(@"2:%@", [NSThread currentThread]);
}];
[operation addExecutionBlock:^() {
NSLog(@"3:%@", [NSThread currentThread]);
}];
[operation addExecutionBlock:^() {
NSLog(@"4:%@", [NSThread currentThread]);
}];
[operation start];
}
/*************************執(zhí)行結果****************************/
2017-09-07 11:02:50.342 TextDemo[1290:497642] 1:<NSThread: 0x61800006d740>{number = 1, name = main}
2017-09-07 11:02:50.343 TextDemo[1290:497642] 3:<NSThread: 0x61800006d740>{number = 1, name = main}
2017-09-07 11:02:50.343 TextDemo[1290:497642] 4:<NSThread: 0x61800006d740>{number = 1, name = main}
2017-09-07 11:02:50.343 TextDemo[1290:497983] 2:<NSThread: 0x618000071240>{number = 5, name = (null)}
當添加多個操作時桩匪,開啟新線程異步執(zhí)行。
2.3友鼻、將操作添加到NSOperationQueue執(zhí)行
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self blcokOperationText];
}
- (void)blcokOperationText
{
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1:%@", [NSThread currentThread]);
}];
// 再添加3操作
[operation addExecutionBlock:^() {
NSLog(@"2:%@", [NSThread currentThread]);
}];
[operation addExecutionBlock:^() {
NSLog(@"3:%@", [NSThread currentThread]);
}];
[operation addExecutionBlock:^() {
NSLog(@"4:%@", [NSThread currentThread]);
}];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperation:operation];
}
/*************************執(zhí)行結果****************************/
2017-09-07 11:07:40.347 TextDemo[1354:525108] 1:<NSThread: 0x61800007ab00>{number = 3, name = (null)}
2017-09-07 11:07:40.347 TextDemo[1354:525105] 4:<NSThread: 0x6100002600c0>{number = 5, name = (null)}
2017-09-07 11:07:40.347 TextDemo[1354:525106] 2:<NSThread: 0x610000261480>{number = 4, name = (null)}
2017-09-07 11:07:40.347 TextDemo[1354:525126] 3:<NSThread: 0x61000007fec0>{number = 6, name = (null)}
根據打印結果傻昙,可以看出新開啟了多個線程執(zhí)行操作,且是異步執(zhí)行的彩扔。
3屋匕、自定義Operation
自定義NSOperation最主要的就是重載-(void)main
這個方法,在這個方法里面添加需要執(zhí)行的操作借杰。當執(zhí)行這個操作時,系統(tǒng)會自動調用-(void)main
這個方法进泼。
#import "CustomOpertaionTest.h"
@implementation CustomOpertaionTest
- (void)main {
// 新建一個自動釋放池蔗衡,避免內存泄露
@autoreleasepool {
// 執(zhí)行的代碼
NSLog(@"這是一個測試:%@",[NSThread currentThread]);
}
}
@end
在主控制器中調用纤虽,一共兩種:一種同步一種異步
//1、直接執(zhí)行绞惦,同步
CustomOpertaionTest *operation = [[CustomOpertaionTest alloc] init];
[operation start];
/*************************執(zhí)行結果****************************/
2017-09-07 11:13:25.149 TextDemo[1354:562140] 這是一個測試:<NSThread: 0x7ff420d28000>{number = 1, name = main}
//2逼纸、添加到隊列,異步
CustomOpertaionTest *operation = [[CustomOpertaionTest alloc] init];
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[operationQueue addOperation:operation];
/*************************執(zhí)行結果****************************/
2017-09-07 11:14:30.500 TextDemo[1354:571046] 這是一個測試:<NSThread: 0x7ff2d0539d70>{number = 2, name = (null)}
四济蝉、NSOperation其它常用方法
1杰刽、取消操作
operation開始執(zhí)行之后, 默認會一直執(zhí)行操作直到完成,我們也可以調用cancel方法中途取消操作
[operation cancel];
這里有一個問題王滤,我們自定義的operation贺嫂,如果支持取消,則應該在重載的main函數(shù)中查詢用戶是否取消了操作雁乡,特別是main函數(shù)中又循環(huán)的時候一定要查詢第喳。然后釋放資源,退出main函數(shù)踱稍。
另外開啟新線程后注意內存管理曲饱,最好在main函數(shù)里面的最外層創(chuàng)建自動釋放池,保證每一個消息循環(huán)都能釋放內存
- (void)main {
// 新建一個自動釋放池珠月,避免內存泄露
@autoreleasepool {
// 執(zhí)行的代碼
NSLog(@"這是一個測試:%@",[NSThread currentThread]);
}
}
2扩淀、NSOperation執(zhí)行完畢后操作
operation.completionBlock = ^() {
// 所有操作執(zhí)行完成后執(zhí)行
};
3、設置最大并發(fā)數(shù)
默認情況最大并發(fā)數(shù)為6啤挎,即同時最多只允許6個線程處于就緒態(tài)驻谆。設置最大并發(fā)數(shù)為1時,程序將串行執(zhí)行
// 最大并發(fā)數(shù)為3
[operationQueue setMaxConcurrentOperationCount:3];
4侵浸、設置依賴
設置依賴來保證執(zhí)行順序旺韭,?如一定要讓操作A執(zhí)行完后,才能執(zhí)行操作B,可以像下面這么寫:
[operationB addDependency:operationA];
但是一定要注意不要A依賴B,然后B又依賴A掏觉,這樣A和B相互依賴造成都不能得到執(zhí)行区端。
如果A和B處于不同的操作隊列,也是可以設置依賴關系的澳腹。