- 全稱是Grand Central Dispatch波桩,可譯為“牛逼的中樞調(diào)度器”
- 純C語言,提供了非常多強(qiáng)大的函數(shù)
GCD的優(yōu)勢 :
- GCD是蘋果公司為多核的并行運(yùn)算提出的解決方案
- GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核查描、四核)
- GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程突委、調(diào)度任務(wù)瘩将、銷毀線程)
- 程序員只需要告訴GCD想要執(zhí)行什么任務(wù)畅厢,不需要編寫任何線程管理代碼
GCD的使用就兩個(gè)步驟 :
- 定制任務(wù)(確定想做的事情)
- 將任務(wù)添加到隊(duì)列中GCD會(huì)自動(dòng)將隊(duì)列中的任務(wù)取出志鹃,放到對應(yīng)的線程中執(zhí)行,任務(wù)的取出遵循隊(duì)列的FIFO(First in First out)原則:先進(jìn)先出恭应,后進(jìn)后出
GCD有兩個(gè)核心概念 :
概念一 : 任務(wù) (執(zhí)行什么操作)
GCD中有兩個(gè)用來執(zhí)行任務(wù)的函數(shù)
// 用同步的方式執(zhí)行任務(wù) :只能在當(dāng)前線程中執(zhí)行任務(wù)聋丝,不具備開啟新線程的能力
// sync: 同步 queue:隊(duì)列 block:任務(wù)
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
// 用異步的方式執(zhí)行任務(wù) : 可以在新的線程中執(zhí)行任務(wù)芳肌,具備開啟新線程的能力
// async: 異步 queue:隊(duì)列 block:任務(wù)
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
概念二 : 隊(duì)列 :(用來存放任務(wù))
GCD中的隊(duì)列可以分為兩大類型
1: 并發(fā)隊(duì)列(Concurrent Dispatch Queue):
- 可以讓多個(gè)任務(wù)并發(fā)同時(shí)執(zhí)行(自動(dòng)開啟多個(gè)線程同時(shí)執(zhí)行任務(wù))
- 并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效
2: 串行隊(duì)列(Serial Dispatch Queue)
- 讓任務(wù)一個(gè)接著一個(gè)地執(zhí)行(一個(gè)任務(wù)執(zhí)行完畢后接谨,再執(zhí)行下一個(gè)任務(wù))
同步笼蛛、異步窝爪、并發(fā)弛车、串行的作用:
- 同步和異步主要影響:能不能開啟新的線程
- 同步:在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力
- 異步:在新的線程中執(zhí)行任務(wù)蒲每,具備開啟新線程的能力
- 并發(fā)和串行主要影響:任務(wù)的執(zhí)行方式
- 并發(fā):多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行
- 串行:一個(gè)任務(wù)執(zhí)行完畢后纷跛,再執(zhí)行下一個(gè)任務(wù)
并發(fā)隊(duì)列與串行隊(duì)列
并發(fā)隊(duì)列
GCD的默認(rèn)已經(jīng)提供了全局的并發(fā)隊(duì)列,供整個(gè)應(yīng)用使用,不需要手動(dòng)創(chuàng)建.
- 獲得全局并發(fā)隊(duì)列
// 用這個(gè)獲得全局并發(fā)隊(duì)列{dispatch_get_global_queue}
// 用這個(gè)獲得優(yōu)先級(jí){DISPATCH_QUEUE_PRIORITY_DEFAULT}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
- 并發(fā)隊(duì)列全局并發(fā)隊(duì)列的優(yōu)先級(jí)
define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默認(rèn)(中)
define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后臺(tái)
并發(fā)隊(duì)列 + 同步函數(shù) : 不會(huì)開啟線程
并發(fā)隊(duì)列 + 異步函數(shù) : 會(huì)開啟線程
串行隊(duì)列
GCD中獲得串行有2種途徑
- 1 - 使用dispatch_queue_create函數(shù)創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("c.w.z", NULL);
串行隊(duì)列 + 異步函數(shù) : 會(huì)開啟線程
串行隊(duì)列 + 同步函數(shù) : 不會(huì)開啟線程
- 2 - 使用主隊(duì)列(跟主線程相關(guān)聯(lián)的隊(duì)列)
主隊(duì)列是GCD自帶的一種特殊的串行隊(duì)列
只要是放在主隊(duì)列中的任務(wù),不管你是同步還是異步函數(shù)全都放在主線程中執(zhí)行
dispatch_queue_t queue = dispatch_get_main_queue();
主要是主隊(duì)列:不會(huì)開啟線程
各種隊(duì)列的執(zhí)行效果
線程間的通訊
從子線程回到主線程
執(zhí)行耗時(shí)的異步操作...
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 回到主線程邀杏,執(zhí)行UI刷新操作
dispatch_async(dispatch_get_main_queue(), ^{
});
});
下載圖片的操作
延時(shí)執(zhí)行
iOS常見的延時(shí)執(zhí)行有2種方式
// 調(diào)用NSObject的方法
// 2秒后再調(diào)用self的run方法
self performSelector:@selector(run) withObject:nil afterDelay:2.0;
// 使用GCD函數(shù)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)),`
dispatch_get_main_queue(), ^{
// 2秒后異步執(zhí)行這里的代碼...
});
// 使用NSTimer
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
只執(zhí)行一次的代碼
// 運(yùn)行時(shí)只執(zhí)行一次
// 使用dispatch_once函數(shù)能保證某段代碼在程序運(yùn)行過程中只被執(zhí)行1次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只執(zhí)行1次的代碼(這里面默認(rèn)是線程安全的)
});
隊(duì)列組
Warning:有這么1種需求:
首先:分別異步執(zhí)行2個(gè)耗時(shí)的操作
其次:等2個(gè)異步操作都執(zhí)行完畢后贫奠,再回到主線程執(zhí)行操作
如果想要快速高效地實(shí)現(xiàn)上述需求唬血,可以考慮用隊(duì)列組
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)^{
// 執(zhí)行1個(gè)耗時(shí)的異步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行1個(gè)耗時(shí)的異步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步操作都執(zhí)行完畢后,回到主線程...
});
代碼詳細(xì)實(shí)現(xiàn):
需求:在子線程中下載2張圖片.分別顯示在屏幕的左右.
分析:由于下載圖片屬于耗時(shí)操作,所以要在子線程中操作.等2張圖片都下載好后,在回到主線程加載.
// 點(diǎn)擊屏幕后調(diào)用
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self group];
}
// 隊(duì)列組
- (void)group
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 創(chuàng)建一個(gè)隊(duì)列組
dispatch_group_t group = dispatch_group_create();
// 1.下載圖片1
dispatch_group_async(group, queue, ^{
// 圖片的網(wǎng)絡(luò)路徑
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
// 加載圖片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成圖片
self.image1 = [UIImage imageWithData:data];
});
// 2.下載圖片2
dispatch_group_async(group, queue, ^{
// 圖片的網(wǎng)絡(luò)路徑
NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
// 加載圖片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成圖片
self.image2 = [UIImage imageWithData:data];
});
// 3.將圖片1唤崭、圖片2合成一張新的圖片
dispatch_group_notify(group, queue, ^{
// 開啟新的圖形上下文
UIGraphicsBeginImageContext(CGSizeMake(100, 100));
// 繪制圖片
[self.image1 drawInRect:CGRectMake(0, 0, 50, 100)];
[self.image2 drawInRect:CGRectMake(50, 0, 50, 100)];
// 取得上下文中的圖片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 結(jié)束上下文
UIGraphicsEndImageContext();
// 回到主線程顯示圖片
dispatch_async(dispatch_get_main_queue(), ^{
// 4.將新圖片顯示出來
self.imageView.image = image;
});
});
}
顯示結(jié)果
barrier
GCD中還有個(gè)用來執(zhí)行任務(wù)的函數(shù)barrier(柵欄/障礙物)
dispatch_barrier_async(dispatch_queue_t queue>, ^(void)block)
- 在前面的任務(wù)執(zhí)行結(jié)束后它才執(zhí)行,而且它后面的的任務(wù)等它執(zhí)行完之后才會(huì)執(zhí)行
- 這個(gè)queue不能是全局的并發(fā)隊(duì)列
他會(huì)在執(zhí)行完 barrier上面的1和2后在執(zhí)行barrier后面的3和4
NSOperation:
是在GCD的基礎(chǔ)上進(jìn)行的一層面向?qū)ο蟮陌b.
核心概念:
任務(wù)和隊(duì)列.和GCD基本上是一樣的,只不過更加的面向?qū)ο?用起來比較爽.
NSOperation的作用
- 配合使用NSOperation和NSOperationQueue也能實(shí)現(xiàn)多線程編程
- NSOperation:就是任務(wù)
- NSOperationQueue:就是隊(duì)列
NSOperation的子類
- NSOperation是個(gè)抽象類拷恨,并不具備封裝操作的能力,必須使用它的子類
使用NSOperation子類的方式有3種:
- NSInvocationOperation,
- NSBlockOperation,
- 自定義子類繼承NSOperation谢肾,實(shí)現(xiàn)內(nèi)部相應(yīng)的方法;
NSOperation和NSOperationQueue實(shí)現(xiàn)多線程的具體步驟:
- 先將需要執(zhí)行的操作封裝到一個(gè)NSOperation對象中,
- 然后將NSOperation對象添加到NSOperationQueue中,
- 系統(tǒng)會(huì)自動(dòng)將NSOperationQueue中的NSOperation取出來,
- 將取出的NSOperation封裝的操作放到一條新線程中執(zhí)行;
1 - NSInvocationOperation(比較少用)
// 1.創(chuàng)建NSInvocationOperation對象
- (id)initWithTarget:(id)target selector:(SEL)sel object:
- (id)arg;
// 2. 調(diào)用start方法開始執(zhí)行操作
- (void)start;
一旦執(zhí)行操作腕侄,就會(huì)調(diào)用target的sel方法
- 默認(rèn)情況下,調(diào)用了start方法后并不會(huì)開一條新線程去執(zhí)行操作芦疏,而是在當(dāng)前線程同步執(zhí)行操作
-
只有將NSOperation放到一個(gè)NSOperationQueue中冕杠,才會(huì)異步執(zhí)行操作
2 - NSBlockOperation
// 1. 創(chuàng)建NSBlockOperation對象
+ (id)blockOperationWithBlock:(void (^)(void))block;
// 2. 通過addExecutionBlock:方法添加更多的操作
- (void)addExecutionBlock:(void (^)(void))block;
-
只要NSBlockOperation封裝的操作數(shù) > 1,就會(huì)異步執(zhí)行操作
3 -自定義NSOperation
- 首先,在控制器中,將操作添加到隊(duì)列中
- 然后會(huì)調(diào)用自定義類的
- main
方法,執(zhí)行- main
方法中的任務(wù)
1.首先新建一個(gè)類,繼承自NSOperation
2.在.m文件中重寫- main
方法 (不是main函數(shù)而是- main
方法)
3.將任務(wù)寫入- main
方法中
NSOperationQueue
- NSOperationQueue的作用
- NSOperation可以調(diào)用
start方法
來執(zhí)行任務(wù)眯分,但默認(rèn)是同步執(zhí)行
的 - 如果將NSOperation添加到
NSOperationQueue(操作隊(duì)列)中
拌汇,系統(tǒng)會(huì)自動(dòng)異步執(zhí)行NSOperation中的操作
// 添加操作到NSOperationQueue中
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block;
NSOperationQueue的隊(duì)列類型
主隊(duì)列:
凡是放到了主隊(duì)列的任務(wù)(NSOperation),都會(huì)放到主線程中執(zhí)行
[NSOperationQueue mainQueue];
其他隊(duì)列(串行/并發(fā)):
同時(shí)包含:串行 / 并發(fā)功能
添加這種隊(duì)列中的任務(wù),就會(huì)自動(dòng)放到子線程中執(zhí)行
[NSOperationQueue alloc] init];
最大并發(fā)數(shù)
同時(shí)執(zhí)行的任務(wù)數(shù):比如同時(shí)開3個(gè)線程執(zhí)行3個(gè)任務(wù),并發(fā)數(shù)就是3
// 最大并發(fā)數(shù)的相關(guān)方法
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
隊(duì)列的取消弊决、暫停、恢復(fù)
// 取消隊(duì)列的所有操作
- (void)cancelAllOperations;
__提示:
也可以調(diào)用NSOperation的- (void)cancel方法取消單個(gè)操作
// 暫停和恢復(fù)隊(duì)列(YES代表暫停隊(duì)列魁淳,NO代表恢復(fù)隊(duì)列)
- (void)setSuspended:(BOOL)b;
- (BOOL)isSuspended;
經(jīng)常通過`- (BOOL)isCancelled`方法檢測操作是否被取消飘诗,對取消做出響應(yīng)
suspended 暫定/掛起
__warning:#cancel
官方建議:執(zhí)行完一段耗時(shí)操作的后面最好加上是否點(diǎn)擊了取消的判斷.
人為控制取消操作.以免,全部執(zhí)行完,還沒有取消掉.
操作優(yōu)先級(jí)
設(shè)置NSOperation在queue中的優(yōu)先級(jí),可以改變操作的執(zhí)行優(yōu)先級(jí)
- (NSOperationQueuePriority)queuePriority;
- (void)setQueuePriority:(NSOperationQueuePriority)p;
##優(yōu)先級(jí)的取值
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
操作依賴
- NSOperation之間可以設(shè)置依賴來保證執(zhí)行順序
比如一定要讓操作A執(zhí)行完后界逛,才能執(zhí)行操作B昆稿,可以這么寫
// 操作B依賴于操作A
operationB addDependency:operationA;
-
可以在不同queue的NSOperation之間創(chuàng)建依賴關(guān)系
操作的監(jiān)聽
// 可以監(jiān)聽一個(gè)操作的執(zhí)行完畢
- (void (^)(void))completionBlock;
- (void)setCompletionBlock:(void (^)(void))block;
自定義NSOperation的步驟很簡單
重寫`-(void)main`方法,在里面實(shí)現(xiàn)想執(zhí)行的任
重寫- (void)main
方法的注意點(diǎn):
- 自己創(chuàng)建自動(dòng)釋放池(因?yàn)槿绻钱惒讲僮飨荩瑹o法訪問主線程的自動(dòng)釋放池)
- 經(jīng)常通過
-(BOOL)isCancelled
方法檢測操作是否被取消溉潭,對取消做出響應(yīng)
GCD和NSOperation的區(qū)別
- GCD是基于C的底層API
- NSOperation屬于object-c類。
- iOS首先引入的是NSOperation少欺,IOS4之后引入了GCD和NSOperationQueue并且其內(nèi)部是用GCD實(shí)現(xiàn)的喳瓣。
NSOperation:
1 - NSOperation擁有更多的函數(shù)可用,具體查看API赞别。
2 - 在NSOperationQueue中畏陕,可以建立各個(gè)NSOperation之間的依賴關(guān)系。
3 - 有KVO仿滔,可以監(jiān)測operation是否
--------------------------正在執(zhí)行isExecuted
惠毁、
--------------------------是否結(jié)束isFinished
、
--------------------------是否取消isCanceld
;
4 - NSOperationQueue可以方便的管理并發(fā)崎页、NSOperation之間的優(yōu)先級(jí)鞠绰。
NSOperationQueue的隊(duì)列類型
- 主隊(duì)列
- [NSOperationQueue mainQueue]
- 凡是添加到主隊(duì)列中的任務(wù)(NSOperation),都會(huì)放到主線程中執(zhí)行
- 非主隊(duì)列(其他隊(duì)列)
- [[NSOperationQueue alloc] init]
- 同時(shí)包含了:串行飒焦、并發(fā)功能
- 添加到這種隊(duì)列中的任務(wù)(NSOperation)蜈膨,就會(huì)自動(dòng)放到子線程中執(zhí)行
GCD:
1 - 主要與block結(jié)合使用。代碼簡潔高效。
2 - GCD也可以實(shí)現(xiàn)復(fù)雜的多線程應(yīng)用丈挟,主要是建立個(gè)個(gè)線程時(shí)間的依賴關(guān)系這類的情況刁卜,但是需要自己實(shí)現(xiàn)相比NSOperation要復(fù)雜。具體使用哪個(gè)曙咽,依需求而定蛔趴。
GCD的隊(duì)列類型
- 并發(fā)隊(duì)列
- 全局
- 自己創(chuàng)建的
- 串行隊(duì)列
- 主隊(duì)列
- 自己創(chuàng)建的
從個(gè)人使用的感覺來看,比較合適的用法是:除了依賴關(guān)系盡量使用GCD例朱,因?yàn)樘O果專門為GCD做了性能上面的優(yōu)化孝情。
GCD實(shí)現(xiàn)單例模式
單例模式 :
其實(shí)就是一種設(shè)計(jì)模式
適用場合
單例模式一般會(huì)在整個(gè)應(yīng)用程序中,共享一份資源(這份資源只需要?jiǎng)?chuàng)建初始化1次)的時(shí)候使用.
比如:
- 一個(gè)APP中有一個(gè)界面是提供公司的信息的,或者創(chuàng)建賬號(hào)的界面
- 那么,無論是點(diǎn)擊添加好友或者登錄,都會(huì)跳轉(zhuǎn)到(訪問)創(chuàng)建賬號(hào)這個(gè)界面
- 那么就可以把這個(gè)界面寫成單例模式,保證屬性以及各種信息都一樣
- 這樣不僅方便地控制了實(shí)例個(gè)數(shù),并節(jié)約系統(tǒng)資源
比如:
Person *p1 = [Person alloc] init];
Person *p2 = [Person alloc] init];
Person *p3 = [Person alloc] init];
Person *p4 = [Person alloc] init];
從上圖我們可以看到:
- 打印出來的p1,p2,p3,p4的內(nèi)存地址是不一樣的
- 因?yàn)槊看握{(diào)用alloc會(huì)分配新的內(nèi)存空間.
- 但是如果實(shí)現(xiàn)了單例模式,就能可以保證在程序運(yùn)行過程中,
- 無論alloc了多少次,Person這個(gè)類都是同一個(gè)對象.
那么我們怎么樣來實(shí)現(xiàn)單例模式呢?
第一種方法:重寫+ allocWithZone
- 之所以我們要實(shí)現(xiàn)單例模式,是希望無論
alloc
了多少次,都只產(chǎn)生一份內(nèi)存
- 那么我們就要從
alloc
這里下手 - 最容易想到的辦法就是在內(nèi)部直接重寫
alloc
- 而且alloc內(nèi)部會(huì)調(diào)用
+ allocWithZone
這個(gè)方法 - 無論別人在外界調(diào)用
alloc
還是調(diào)用+ allocWithZone
都會(huì)來到+ allocWithZone
這個(gè)方法 - 所以要重寫
+ allocWithZone
- 上圖可以看出我們打印出來的內(nèi)存地址的結(jié)果都是一樣的.
接下來我們來通過打印屬性值來驗(yàn)證下:
第二種方法:[UIApplication sharedApplication]
第三種方法:copy
那么,我們應(yīng)該這樣做
- 看到這里可能有一個(gè)疑問,這里直接返回_Person的話值不會(huì)為空么?
- 答案是肯定不會(huì)為空的.
- 因?yàn)檎{(diào)用copy方法,肯定是有對象在調(diào)用的
- 所以對象都有值了,那么這個(gè)方法里面肯定也是回有值的
將以上的結(jié)合起來才屬于一套完整的單例模式
那么以后一個(gè)工程中可能會(huì)有很多單例,總不能一個(gè)一個(gè)改吧?
那么我們可以將單例弄成宏的形式,以后遇到了直接拖入工程就可以了
那么 我們來看下效果:
.h文件
.m文件
實(shí)現(xiàn)的效果
單例模式在ARC\MRC環(huán)境下的寫法有所不同
需要編寫2套不同的代碼
可以用宏判斷是否為ARC環(huán)境
#if __has_feature(objc_arc)
// ARC
#else
// MRC
#endif
ARC中洒嗤,單例模式的實(shí)現(xiàn)
1. 在.m中保留一個(gè)全局的`static`的實(shí)例
static id _instance;
// 重寫allocWithZone:方法箫荡,在這里創(chuàng)建唯一的實(shí)例(注意線程安全)
+(id)allocWithZone:(struct _NSZone *)zone
{
@synchronized(self) {
if (!_instance) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
2. 提供1個(gè)類方法讓外界訪問唯一的實(shí)例
+ (instancetype)sharedSoundTool{
@synchronized(self) {
if (!_instance) {
_instance = [[self alloc] init];
}
}
return _instance;
}
3. 實(shí)現(xiàn)copyWithZone:方法
- (id)copyWithZone:(struct _NSZone *)zone
{
return _instance;
}
MRC中,單例模式的實(shí)現(xiàn)(比ARC多了幾個(gè)步驟)
// 實(shí)現(xiàn)內(nèi)存管理方法
- (id)retain {
return self;
}
- (NSUInteger)retainCount{
return 1;
}
- (oneway void)release {
}
- (id)autorelease {
return self;
}