在iOS開發(fā)中我們經(jīng)常會(huì)用到多線程來處理一些業(yè)務(wù)褐荷,那么iOS里有哪些實(shí)現(xiàn)多線程的方式呢嘹悼?
- NSTread:封裝程度最小、最輕量級(jí)其监,開銷較大限匣。
- GCD(Grand Central Dispatch):內(nèi)部效率優(yōu)化,提供簡潔的C語言接口米死,更加簡單高效。
- NSOperation:基于GCD的一個(gè)抽象基類峦筒,不需要管理線程的生命周期和同步,比GCD可控性強(qiáng)卤材。
一峦失、NSTread
NSTread封裝程度最小、最輕量級(jí)的多線程編程接口帆精,它使用更加靈活,但需要手動(dòng)管理線程的生命周期实幕、線程同步和線程加鎖等堤器,開銷較大闸溃。
/** NSThread 靜態(tài)工具方法 **/
//1、是否開啟了多線程
BOOL isMultiThread = [NSThread isMultiThreaded];
//2辉川、獲取當(dāng)前線程
NSThread *currentThread = [NSThread currentThread];
//3、獲取主線程
NSThread *mainThread = [NSThread mainThread];
//4府蛇、睡眠當(dāng)前線程
//4.1屿愚、線程睡眠5s
[NSThread sleepForTimeInterval:5];
//4.2、線程睡眠到指定時(shí)間妆距,效果同上
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:5]];
//5、退出當(dāng)前線程蚪黑,注意不要在主線程調(diào)用中剩,防止主線程被kill掉
[NSThread exit];
NSThread的使用比較簡單,可以動(dòng)態(tài)創(chuàng)建并初始化NSThread對(duì)象结啼,對(duì)其進(jìn)行設(shè)置并啟動(dòng);也可以通過NSThread的靜態(tài)方法快速創(chuàng)建并啟動(dòng)新線程澡腾;
/** NSTread 線程對(duì)象的基本創(chuàng)建,target為入口方法所在的對(duì)象动分,selector為線程入口方法 **/
//1红选、線程實(shí)例對(duì)象創(chuàng)建與設(shè)置
NSThread *newThread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
//設(shè)置線程優(yōu)先級(jí)threadPriority(0~1.0),該屬性即將被拋棄坟乾,將使用qualityOfService代替
//newThread.threadPriority = 1.0;
newThread.qualityOfService = NSQualityOfServiceUserInteractive;
//開啟線程
[newThread start];
//2蝶防、靜態(tài)方法快速創(chuàng)建并開啟新線程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
[NSThread detachNewThreadWithBlock:^{
NSLog(@"block run");
}];
此外NSObject提供了隱式快速創(chuàng)建NSThread線程的performSelector系列方法。
/** NSObject 基類隱式創(chuàng)建線程的一些靜態(tài)工具方法 **/
//1殷费、在當(dāng)前線程上執(zhí)行方法,延遲2s
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
//2详羡、在指定線程上執(zhí)行方法,不等待當(dāng)前線程
[self performSelector:@selector(run) onThread:newThread withObject:nil waitUntilDone:NO];
//3实柠、后臺(tái)異步執(zhí)行方法
[self performSelectorInBackground:@selector(run) withObject:nil];
//4、在主線程上執(zhí)行方法
[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];
二草则、GCD
GCD(Grand Central Dispatch)又叫大中央調(diào)度登钥,它對(duì)線程操作進(jìn)行了封裝,加入了很多新的特性牧牢,內(nèi)部進(jìn)行了效率優(yōu)化,提供了簡潔的C語言接口伯铣,使用更加簡單高效轮纫,也是蘋果公司推薦的方式。
GCD的用法特別靈活放前,在下一篇將詳細(xì)講解一下它的用法;這里主要掌握點(diǎn)分為以下幾個(gè)方面:
1凭语、串行隊(duì)列與并發(fā)隊(duì)列dispatch_queue_t撩扒;
/*
創(chuàng)建隊(duì)列
DISPATCH_QUEUE_SERIAL 表示串行隊(duì)列,隊(duì)列內(nèi)任務(wù)一個(gè)接一個(gè)的執(zhí)行炒辉,按照先進(jìn)先出(FIFO)的順序執(zhí)行
DISPATCH_QUEUE_CONCURRENT 表示并發(fā)隊(duì)列,隊(duì)列內(nèi)任務(wù)可同時(shí)并列執(zhí)行黔寇,任務(wù)之間不會(huì)相互等待斩萌,執(zhí)行順序不可預(yù)測(cè)
*/
// 串行隊(duì)列的創(chuàng)建方法
dispatch_queue_t queueSerial = dispatch_queue_create("com.jzsec.GCDtest", DISPATCH_QUEUE_SERIAL);
// 并發(fā)隊(duì)列的創(chuàng)建方法
dispatch_queue_t queueCon = dispatch_queue_create("com.jzsec.GCDtest", DISPATCH_QUEUE_CONCURRENT);
2状囱、同步dispatch_sync與異步dispatch_async派發(fā)任務(wù);
dispatch_sync(queue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(queue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
3袭艺、dispatch_once_t 只執(zhí)行一次;
/**
* 一次性代碼(只執(zhí)行一次)dispatch_once
能保證某段代碼在程序運(yùn)行過程中只被執(zhí)行1次猾编,并且即使在多線程的環(huán)境下,dispatch_once也可以保證線程安全轰传。
*/
- (void)once {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只執(zhí)行1次的代碼(這里面默認(rèn)是線程安全的)
NSLog(@"dispatch_once");
});
}
4瘪撇、dispatch_after 延后執(zhí)行;
/**
* 延時(shí)執(zhí)行方法 dispatch_after
dispatch_after函數(shù)并不是在指定時(shí)間之后才開始執(zhí)行處理恕曲,而是在指定時(shí)間之后將任務(wù)追加到主隊(duì)列中渤涌。嚴(yán)格來說,這個(gè)時(shí)間并不是絕對(duì)準(zhǔn)確的实蓬,但想要大致延遲執(zhí)行任務(wù),dispatch_after函數(shù)是很有效的调鬓。
*/
- (void)after {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"asyncMain---begin");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2.0秒后異步追加任務(wù)代碼到主隊(duì)列练俐,并開始執(zhí)行
NSLog(@"after---%@",[NSThread currentThread]); // 打印當(dāng)前線程
});
}
5、dispatch_group_t 組調(diào)度燕锥;
/**
* 隊(duì)列組 dispatch_group_notify
當(dāng)group所有任務(wù)都執(zhí)行完成之后悯蝉,才執(zhí)行dispatch_group_notify block 中的任務(wù)。
*/
- (void)groupNotify {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步任務(wù)1暇榴、任務(wù)2都執(zhí)行完畢后,回到主線程執(zhí)行下邊任務(wù)
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
NSLog(@"group---end");
});
}
三婆硬、NSOperation
基于GCD的一個(gè)抽象基類,將線程封裝成要執(zhí)行的操作彬犯,不需要管理線程的生命周期和同步查吊,比GCD可控性強(qiáng),例如加入操作依賴控制操作執(zhí)行順序宋列、設(shè)置操作隊(duì)列最大可并發(fā)執(zhí)行的才做個(gè)數(shù)和取消執(zhí)行等评也。
/** NSInvocationOperation 初始化 **/
NSInvocationOperation *invoOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
[invoOperation start];
/** NSBlockOperation 初始化 **/
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"NSBlockOperationA:%@", [NSThread currentThread]);
}];
//blockOperation可以后續(xù)繼續(xù)添加block執(zhí)行塊,操作執(zhí)行后會(huì)在不同的線程并發(fā)執(zhí)行這些執(zhí)行塊嘹叫。
[blockOperation addExecutionBlock:^{
NSLog(@"NSBlockOperationB:%@", [NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"NSBlockOperationC:%@", [NSThread currentThread]);
}];
[blockOperation start];
/** 獲取主隊(duì)列(主線程) **/
NSOperationQueue *queue = [NSOperationQueue mainQueue];
//創(chuàng)建a诈乒、b、c操作
NSOperation *a = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"OperationA");
}];
NSOperation *b = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"OperationB");
}];
NSOperation *c = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"OperationC");
}];
//添加操作依賴喂饥,c依賴于a和b,這樣c一定會(huì)在a和b完成之后才執(zhí)行员帮,即順序?yàn)锳导饲、B、C
[c addDependency:a];
[c addDependency:b];
//添加操作a硝岗、b、c 到操作隊(duì)列queue(特意將c在a和b之前添加)
[queue addOperation:c];
[queue addOperation:a];
[queue addOperation:b];
四型檀、線程同步
對(duì)于UI的更新代碼,必須要寫在主線程上執(zhí)行才會(huì)及時(shí)有效听盖;當(dāng)當(dāng)前代碼不在主線程時(shí)裂七,需要將UI更新的部分代碼單獨(dú)同步到主線程背零。
同步的方法有三種:
- NSThread類的performSelectorOnMainThread方法
- NSOperation類的mainQueue主隊(duì)列
- GCD的dispatch_get_main_queue()獲取主隊(duì)列
推薦直接使用GCD的方法:
dispatch_async(dispatch_get_main_queue( ), ^{
//刷新UI的代碼
});
在iOS實(shí)際開發(fā)中无埃,還是使用GCD的情況比較多,這里只是簡單介紹對(duì)了三種多線程實(shí)現(xiàn)的方式录语,在下一篇將詳細(xì)介紹一下GCD的用法和注意事項(xiàng)澎埠。