線程
- 保證主線程的正常運(yùn)行,主線程主要顯示和處理UI
- 當(dāng)你處理時(shí)間較長的會(huì)阻塞主線程的操作的時(shí)候,要用到分線程來處理
比如
- 下載大的文件 (耗時(shí))
- 數(shù)據(jù)量較大的數(shù)據(jù)庫操作(耗時(shí))
- 文件的讀寫(耗時(shí))
iOS多線程編程技術(shù)有三種
1.NSThread
2.NSOperation
3.GCD(全稱 Grand Central Dispatch)
這三種編程方式從上到下,抽象度層次是從低到高的,抽象度越高的使用越簡(jiǎn)單凌唬,也是Apple最推薦使用的
NSThread 使用
優(yōu)點(diǎn):NSThread 比其他兩個(gè)輕量級(jí)
缺點(diǎn):需要自己管理線程的生命周期测垛,線程同步锯七,線程同步對(duì)數(shù)據(jù)的加鎖會(huì)有一定的系統(tǒng)開銷
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
-(void)viewDidLoad
{
[super viewDidLoad];
// 1.第一種方式(可控的 實(shí)例方法)
NSThread * thread = [[NSThread alloc]initWithTarget:self selector:@selector(threadMethod) object:nil];
[thread start]; //開始
// 2.第二種方式
[NSThread detachNewThreadWithBlock:^{
NSLog(@"當(dāng)前在%@線程",[NSThread currentThread]);
}];
[NSThread detachNewThreadSelector:@selector(threadMethod) toTarget:self withObject:nil];
// 3.第三種方式 靜態(tài)方法
[self performSelectorInBackground:@selector(threadMethod) withObject:nil];
}
-(void)threadMethod
{
@autoreleasepool // 自動(dòng)釋放池(目前版本不需要?jiǎng)?chuàng)建了)
{
// 判斷 當(dāng)前執(zhí)行這些代碼的線程是不是主線程筑累;
BOOL result = [NSThread isMainThread];
NSLog(@"是否主線程:%d",result);
// 讓線程休眠10s
[NSThread sleepForTimeInterval:10];
/** 回到主線程
* 當(dāng)前線程為主線程的時(shí)候婆廊,waitUntilDone:YES參數(shù)無效
* YES會(huì)阻塞住線程,直到調(diào)用方法完成
* NO不會(huì)阻塞線程筹我,會(huì)繼續(xù)執(zhí)行
*/
[self performSelectorOnMainThread:@selector(onMainThread) withObject:nil waitUntilDone:NO];
}
}
-(void)onMainThread
{
NSLog(@"回到主線程");
}
@end
NSOperation 使用
NSOperation不需要自己創(chuàng)建線程,只需要關(guān)注在分線程中完成的代碼,然后把NSOperation放到NSOperationQueue中即可,NSOperationQueue會(huì)把代碼放到分線程中執(zhí)行
NSOperation是個(gè)抽象類,不具備封裝操作的能力踩娘,所以必須使用它的子類
- NSInvocationOperation
- NSBlockOperation
- 自定義的子類繼承NSOperation翘紊,實(shí)現(xiàn)內(nèi)部相應(yīng)的方法
綜上所述:優(yōu)點(diǎn)就是不需要我們關(guān)心線程的管理和數(shù)據(jù)的同步等事情踪宠,我們只需要把精力放在自己需要執(zhí)行的操作上
#import "ViewController.h"
#import "WYSOper.h"
@interface ViewController ()
@end
@implementation ViewController
-(void)viewDidLoad
{
[super viewDidLoad];
[self useOperationQueue];
}
#pragma mark - NSInvocationOperation
-(void)useInvocationOperation
{
NSInvocationOperation * invOper = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(oper1) object:nil];
[invOper start];
}
-(void)oper1
{
NSLog(@"oper1: %@",[NSThread isMainThread]?@"主線程":@"分線程");
}
#pragma mark - NSBlockOperation
-(void)useBlockOperation
{
/**
* NSBlockOperation 添加的主任務(wù)是在主線程中執(zhí)行的, 添加的其他額外任務(wù)是在分線程中執(zhí)行的
*/
NSBlockOperation * blockOper = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"BlockOper: %@",[NSThread isMainThread]?@"主線程":@"分線程");
}];
//在block操作中添加額外任務(wù)
[blockOper addExecutionBlock:^{
NSLog(@"BlockOperOther: %@",[NSThread isMainThread]?@"主線程":@"分線程");
}];
[blockOper start];
}
#pragma mark - NSOperationQueue
-(void)useOperationQueue
{
//自定義的子類繼承NSOperation
WYSOper * operA = [[WYSOper alloc]init];
operA.threadName = @"T1";
WYSOper * operB = [[WYSOper alloc]init];
operB.threadName = @"T2";
WYSOper * operC = [[WYSOper alloc]init];
operC.threadName = @"T3";
//創(chuàng)建隊(duì)列
NSOperationQueue * operQueue = [[NSOperationQueue alloc]init];
//設(shè)置隊(duì)列中最大的線程并發(fā)數(shù) (隊(duì)列一次最多只能執(zhí)行多少個(gè)任務(wù))
[operQueue setMaxConcurrentOperationCount:10];
//添加操作之間的依賴關(guān)系 (任務(wù)A依賴于任務(wù)B涯雅,任務(wù)A必須等到任務(wù)B完成之后才能執(zhí)行)
//[operA addDependency:operB];
//設(shè)置優(yōu)先級(jí)
operC.queuePriority = NSOperationQueuePriorityLow;
operB.queuePriority = NSOperationQueuePriorityVeryHigh;
//把操作對(duì)象放入到操作隊(duì)列中
[operQueue addOperation:operA];
[operQueue addOperation:operB];
[operQueue addOperation:operC];
//取消隊(duì)列中的任務(wù)
//[operQueue cancelAllOperations];
//暫停隊(duì)列
//[operQueue setSuspended:YES];
//繼續(xù)
//[operQueue setSuspended:NO];
}
@end
自定義的WYSOper類(WYSOper.m)
#import "WYSOper.h"
@implementation WYSOper
//重寫Operation的main方法
-(void)main
{
NSLog(@"線程名稱:%@ - 當(dāng)前在:%@",self.threadName,[NSThread isMainThread]?@"主線程":@"分線程");
}
@end
GCD(Grand Central Dispatch)
GCD是蘋果公司開發(fā)的多核編程技術(shù),以優(yōu)化的應(yīng)用程序支持多核心處理器和其他的對(duì)稱多處理系統(tǒng)的系統(tǒng)爬立。這建立在任務(wù)并行執(zhí)行的線程池模式的基礎(chǔ)上的
GCD的工作原理
- 讓程序平行排隊(duì)的特定任務(wù)的止,根據(jù)可用的處理資源,安排他們?cè)谌魏慰捎玫奶幚砥骱诵纳蠄?zhí)行任務(wù)
- 一個(gè)任務(wù)可以是一個(gè)函數(shù)或者是一個(gè)block。 GCD的底層依然是用線程實(shí)現(xiàn)砸民,不過這樣可以讓程序員不用關(guān)注實(shí)現(xiàn)的細(xì)節(jié)
GCD的任務(wù)
-
disptach_sync
沒有創(chuàng)建線程的欲望秒际,就在當(dāng)前線程執(zhí)行
最主要的目的锐峭,阻塞并行隊(duì)列任務(wù)的執(zhí)行具温,只有當(dāng)前的同步任務(wù)執(zhí)行完畢后,后續(xù)的任務(wù)才能夠執(zhí)行
應(yīng)用場(chǎng)景:用戶登錄龄寞! -
dispatch_async
有創(chuàng)建線程的欲望,但是創(chuàng)建多少條線程拂封,取決與隊(duì)列的類型
GCD的隊(duì)列
-
串行隊(duì)列
類似于跑步钟病,只有一條跑道,最多能夠有兩條
如果存在異步任務(wù)簿煌,就會(huì)在新線程中執(zhí)行異步任務(wù),而同步任務(wù)依舊在當(dāng)前線程中執(zhí)行 -
并行隊(duì)列
類似與賽跑,具體跑道的數(shù)量办斑,由系統(tǒng)決定
#pragma mark - 串行隊(duì)列
-(void)useQueueSerial
{
//創(chuàng)建一個(gè)串行隊(duì)列
//GCD 中有一個(gè)類似于operationQueue的隊(duì)列 dispatch_queue_t
dispatch_queue_t serialQueue = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
//同步sync放入操作內(nèi)容
dispatch_sync(serialQueue, ^{
NSLog(@"串行 - sync - 當(dāng)前在%@",[NSThread isMainThread]?@"主線程":@"分線程");
});
//異步async放入操作內(nèi)容
dispatch_async(serialQueue, ^{
NSLog(@"串行 - async - 當(dāng)前在%@",[NSThread isMainThread]?@"主線程":@"分線程");
});
dispatch_suspend(serialQueue); //暫停
dispatch_resume(serialQueue); //繼續(xù)
/**
* 在串行隊(duì)列中的任務(wù),都是需要等待的,必須等到上個(gè)任務(wù)執(zhí)行完畢之后,才能執(zhí)行下個(gè)任務(wù)
* 如果任務(wù)是同步放入到串行隊(duì)列中的,任務(wù)在主線程中執(zhí)行;異步放入的任務(wù),在分線程中執(zhí)行
* 隊(duì)列可以暫停
*/
}
#pragma mark - 并行隊(duì)列
-(void)useQueueConcurrent
{
//創(chuàng)建一個(gè)并行隊(duì)列
//dispatch_queue_t conQueue = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
/**
* 系統(tǒng)提供的有一個(gè)并行隊(duì)列外恕,可以直接使用
* 1. 隊(duì)列的優(yōu)先級(jí) HIGH > DEFAULT > LOW > BACKGROUD
* 2. 系統(tǒng)預(yù)留的參數(shù)位 0
*/
dispatch_queue_t conQueueSystem = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//同步sync放入操作內(nèi)容
dispatch_sync(conQueueSystem, ^{
NSLog(@"并行 - sync - 當(dāng)前在%@",[NSThread isMainThread]?@"主線程":@"分線程");
});
//異步async放入操作內(nèi)容
dispatch_async(conQueueSystem, ^{
NSLog(@"并行 - async - 當(dāng)前在%@",[NSThread isMainThread]?@"主線程":@"分線程");
});
/**
* 在并行隊(duì)列中如果同步放入任務(wù),任務(wù)也需要等待,必須等到上一個(gè)任務(wù)結(jié)束之后才執(zhí)行下個(gè)任務(wù),在主線程中執(zhí)行
* 異步放入任務(wù),任務(wù)是并發(fā)執(zhí)行,在分線程中執(zhí)行
* 并行隊(duì)列不可以暫停
*/
}
#pragma mark - 主線程隊(duì)列
-(void)getMainQueue
{
//獲取到主線程隊(duì)列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//往主線程隊(duì)列中異步放入任務(wù)
dispatch_async(mainQueue, ^{
NSLog(@"主線程 - %@",[NSThread isMainThread]?@"主線程":@"分線程");
});
}
2017.1.20更新
-(void)viewDidLoad
{
[super viewDidLoad];
[[self class] networkRequestThread];
}
//創(chuàng)建一個(gè)網(wǎng)絡(luò)通信線程
+(NSThread *)networkRequestThread
{
static NSThread * _networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
//線程處理
+(void)networkRequestThreadEntryPoint:(id)__unused object
{
@autoreleasepool
{
[[NSThread currentThread] setName:@"線程名稱"];
NSRunLoop * runLoop = [NSRunLoop currentRunLoop]; //設(shè)置runloop
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
線程鎖
多個(gè)線程同時(shí)訪問公共資源,并對(duì)公共資源進(jìn)行修改乡翅,容易出現(xiàn)數(shù)據(jù)錯(cuò)亂的情況鳞疲,為解決此問題,需要使用線程鎖
#pragma mark - 線程鎖
-(void)useThreadLock
{
// 第一種方式
NSLock * mLock = [[NSLock alloc]init];
[mLock lock]; //加鎖
[mLock unlock]; //解鎖
// 第二種方式
// 當(dāng)程序運(yùn)行到@synchronized(self),先要判斷別的線程有沒有在使用同步對(duì)象,如果正在使用,當(dāng)前線程需要等待,一直等到別的線程把同步塊執(zhí)行完畢,釋放同步對(duì)象之后,當(dāng)前線程才能執(zhí)行
@synchronized (self)
{
//...
}
}
注意:Timer不能在分線程中直接使用蠕蚜,需要手動(dòng)開啟RunLoop
個(gè)人微信公眾號(hào):iapp666666
GitHub:https://github.com/29745560