最近利用晚上的空閑時(shí)間,整理了一下多線程相關(guān)的知識(shí),幾經(jīng)更改,就變成你現(xiàn)在看到的模樣了(如有失誤之處,還請(qǐng)不吝賜教)。把多線程相關(guān)的知識(shí)總結(jié)在這里,只希望對(duì)看到的朋友們有所幫助!文章有些長(zhǎng),涵蓋的知識(shí)點(diǎn)也比較多,希望朋友們能夠耐心讀完哈!我們這篇文章主要包括以下幾個(gè)模塊:
- (一)iOS開(kāi)發(fā)之多線程 基礎(chǔ)知識(shí)
- (二)iOS開(kāi)發(fā)之多線程 NSThread
- (三)iOS開(kāi)發(fā)之多線程 NSOperation
- (四)iOS開(kāi)發(fā)之多線程 GCD
- (五)iOS開(kāi)發(fā)之多線程 線程間的通信
下面我們開(kāi)始了解 iOS 多線程的基礎(chǔ)知識(shí),如果在此之前對(duì)多線程已經(jīng)有所了解的朋友,大可跳過(guò)這一部分,直接看后邊三種多線程編程的 demo 即可!
(一)iOS 開(kāi)發(fā)之多線程 基礎(chǔ)知識(shí)
1.多線程簡(jiǎn)介:
一個(gè)進(jìn)程要想執(zhí)行任務(wù), 必須得有線程(每個(gè)進(jìn)程至少要有一條線程), 線程是進(jìn)程的基本執(zhí)行單元, 一個(gè)進(jìn)程(程序)的所有任務(wù)都在線程中執(zhí)行.
2.多線程的原理:
同一時(shí)間, CPU 只能處理一條線程,只有一條線程在工作.
多線程并發(fā)(同時(shí))執(zhí)行, 其實(shí)是 CPU 快速地在多條線程之間調(diào)度.
如果 CPU 調(diào)度線程的時(shí)間足夠快,就造成了多線程并發(fā)執(zhí)行的假象.
3.多線程的優(yōu)缺點(diǎn):
多線程的優(yōu)點(diǎn):
能適當(dāng)提高程序的執(zhí)行效率.
能適當(dāng)提高資源利用率(CPU、內(nèi)存利用率).
多線程的缺點(diǎn):
開(kāi)啟線程需要占用一定的內(nèi)存空間(默認(rèn)情況下,主線程占用1M,子線程占用512KB),如果開(kāi)啟大量的線程,會(huì)占用大量的內(nèi)存空間,降低程序的性能.
4.多線程在 iOS 開(kāi)發(fā)中的應(yīng)用:
主線程:一個(gè) iOS 程序運(yùn)行后,默認(rèn)會(huì)開(kāi)啟1條線程,稱(chēng)為“主線程”或“ UI 線程”.
主線程的主要作用:刷新 UI 界面、處理 UI 事件(比如點(diǎn)擊事件、滾動(dòng)事件、拖拽事件等).
5.iOS 中三種多線程編程的技術(shù),分別是:
(一)NSThread
(二)Cocoa NSOperation
(三)GCD(全稱(chēng):Grand Central Dispatch)
這三種編程方式從上到下,抽象度層次是從低到高的,抽象度越高的使用越簡(jiǎn)單,也是Apple最推薦使用的。
三種方式的優(yōu)缺點(diǎn)介紹:
NSThread:
優(yōu)點(diǎn):NSThread 比其他兩個(gè)輕量級(jí)
缺點(diǎn):需要自己管理線程的生命周期,線程同步。線程同步對(duì)數(shù)據(jù)的加鎖會(huì)有一定的系統(tǒng)開(kāi)銷(xiāo)。
Cocoa NSOperation
優(yōu)點(diǎn):不需要關(guān)心線程管理,數(shù)據(jù)同步的事情,可以把精力放在自己需要執(zhí)行的操作上。
Cocoa operation 相關(guān)的類(lèi)是 NSOperation ,NSOperationQueue。
NSOperation是個(gè)抽象類(lèi),使用它必須用它的子類(lèi),可以實(shí)現(xiàn)它或者使用它定義好的兩個(gè)子類(lèi):NSInvocationOperation 和 NSBlockOperation。
創(chuàng)建NSOperation子類(lèi)的對(duì)象,把對(duì)象添加到NSOperationQueue隊(duì)列里執(zhí)行。
GCD
Grand Central Dispatch (GCD)是Apple開(kāi)發(fā)的一個(gè)多核編程的解決方法。在iOS4.0開(kāi)始之后才能使用。GCD是一個(gè)替代諸如NSThread, NSOperationQueue, NSInvocationOperation等技術(shù)的很高效和強(qiáng)大的技術(shù)。
對(duì) iOS 基礎(chǔ)知識(shí)有了一定了解之后,下邊我們開(kāi)始看多線程相關(guān)的一些 demo, 建議朋友們?cè)囋囅逻叺?demo,這樣更方便理解!如有疑問(wèn),或發(fā)現(xiàn)某些 demo 寫(xiě)的有誤,還請(qǐng)朋友們告知!
下邊我們開(kāi)始了解NSThread,NSThread 其實(shí)用的真不多,主要還是用另外兩種,不過(guò)作為 iOS 開(kāi)發(fā)者,還是應(yīng)該對(duì)它有些了解的,下邊是 NSThread 相關(guān)的 demo !
(二)iOS 開(kāi)發(fā)之多線程 NSThread
一、NSThread 初始化
1.動(dòng)態(tài)方法
- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument
// 初始化線程
NSThread* thread = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething:)object:nil];
// 線程名字
thread.name = @"MYThread";
// 啟動(dòng)線程
[thread start];
2.靜態(tài)方法
+ (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument
[NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];
// 調(diào)用完畢后,會(huì)馬上創(chuàng)建并開(kāi)啟新線程
3.隱式創(chuàng)建線程的方法:
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;
[self performSelectorInBackground:@selector(run) withObject:nil];
提示朋友們一下:第一種方式會(huì)直接創(chuàng)建線程并且開(kāi)始運(yùn)行線程,第二種方式是先創(chuàng)建線程對(duì)象,然后再運(yùn)行線程操作,在運(yùn)行線程操作前可以設(shè)置線程的優(yōu)先級(jí)等線程信息
二、線程間的通信
//在主線程上執(zhí)行操作
[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES];
//在當(dāng)前線程執(zhí)行操作
[self performSelector:@selector(run) withObject:nil];
//在指定線程上執(zhí)行操作
[self performSelector:@selector(run) onThread:thread withObject:nil waitUntilDone:YES];
三、一些簡(jiǎn)單方法
//獲取當(dāng)前線程
NSThread *thread = [NSThread currentThread];
//獲取主線程
NSThread *main = [NSThread mainThread];
//睡眠,相當(dāng)于暫停
[NSThread sleepForTimeInterval:3];
NSThread 就說(shuō)到這,一起再看下NSOperation, NSOperation 在開(kāi)發(fā)中用的還是挺多的,雖然蘋(píng)果建議使用 GCD ,但依然有開(kāi)發(fā)者覺(jué)得 NSOperation 比 GCD 更好用!仁者見(jiàn)仁智者見(jiàn)智吧!本小白覺(jué)得如果能掌握還是都掌握的好!
(三)iOS開(kāi)發(fā)之多線程 NSOperation
一、創(chuàng)建線程的方式
使用NSOperation創(chuàng)建線程的方式有3種:
(1)NSInvocationOperation 方式
(2)NSBlockOperation 方式
(3)自定義子類(lèi)繼承NSOperation,實(shí)現(xiàn)內(nèi)部相應(yīng)的?法
下面是這三種方式創(chuàng)建線程的具體方法:
1.NSInvocationOperation
- (void)invocationOperation
{
//創(chuàng)建NSInvocationOperation對(duì)象
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
//開(kāi)始執(zhí)行操作
[operation start];
}
//一旦執(zhí)行操作,就會(huì)調(diào)用target的run方法
提示:默認(rèn)情況下,調(diào)用了start方法后并不會(huì)開(kāi)一條新線程去執(zhí)行操作,只有將NSOperation放到一個(gè)NSOperationQueue中,才會(huì)異步執(zhí)行操作.
2.NSBlockOperation
- (void)blockOperation
{
//創(chuàng)建NSBlockOperation對(duì)象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
// 主線程
NSLog(@"1---%@", [NSThread currentThread]);
}];
// 添加任務(wù)(在子線程執(zhí)行)
[operation addExecutionBlock:^{
NSLog(@"2---%@", [NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"3---%@", [NSThread currentThread]);
}];
//開(kāi)啟執(zhí)行操作
[operation start];
}
提示:只要NSBlockOperation封裝的操作數(shù) > 1,就會(huì)異步執(zhí)行操作
3.自定義子類(lèi)繼承自NSOperation
自定義NSOperation子類(lèi)需要重寫(xiě) - (void)main方法
- (void)main
{
// 新建一個(gè)自動(dòng)釋放池,如果是異步執(zhí)行操作,那么將無(wú)法訪問(wèn)到主線程的自動(dòng)釋放池
@autoreleasepool {
for (NSInteger i = 0; i<100; i++) {
NSLog(@"1 - %ld - %@", i, [NSThread currentThread]);
}
//檢測(cè)操作是否被取消,對(duì)取消做出響應(yīng)
if (self.isCancelled) return;
for (NSInteger i = 0; i<100; i++) {
NSLog(@"2 - %ld - %@", i, [NSThread currentThread]);
}
if (self.isCancelled) return;
for (NSInteger i = 0; i<100; i++) {
NSLog(@"3 - %ld - %@", i, [NSThread currentThread]);
}
if (self.isCancelled) return;
}
}
二、NSOperationQueue的應(yīng)用
一個(gè)NSOperation對(duì)象可以通過(guò)調(diào)用start方法來(lái)執(zhí)行任務(wù),默認(rèn)是同步執(zhí)行的.也可以將NSOperation添加到一個(gè)NSOperationQueue中去執(zhí)行,而且是異步執(zhí)行的.
1.NSOperationQueue簡(jiǎn)單創(chuàng)建
//第一種方式
- (void)operationQueue
{
// 創(chuàng)建NSOperationQueue隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 創(chuàng)建NSInvocationOperation對(duì)象
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run1) object:nil];
NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run2) object:nil];
// 創(chuàng)建NSBlockOperation對(duì)象
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1 --- %@", [NSThread currentThread]);
}];
[operation3 addExecutionBlock:^{
NSLog(@"2 --- %@", [NSThread currentThread]);
}];
NSBlockOperation *operation4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"3 --- %@", [NSThread currentThread]);
}];
// 添加任務(wù)到隊(duì)列中
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
[queue addOperation:operation4];
}
//第二種方式
- (void)operationQueue2
{
// 創(chuàng)建NSOperationQueue隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
NSLog(@"1 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"2 --- %@", [NSThread currentThread]);
}];
}
2.NSOperation中的操作依賴(lài)
NSOperation之間可以設(shè)置依賴(lài)來(lái)保證執(zhí)行順序,比如一定要讓操作A執(zhí)行完后,才能執(zhí)行操作B,可以這么寫(xiě)[operationB addDependency:operationA]; // 操作B依賴(lài)于操作operationA,另外,通過(guò)removeDependency方法可以刪除依賴(lài)對(duì)象.
#pragma mark - 依賴(lài)操作調(diào)整執(zhí)行順序
- (void)NSOperationTest1 {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"~~1~~%@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 5; i ++) {
NSLog(@"~~2~~%@~~%d~~", [NSThread currentThread], i);
}
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"~~3~~%@", [NSThread currentThread]);
}];
[op2 addExecutionBlock:^{
NSLog(@"~~4~~%@", [NSThread currentThread]);
}];
op3.completionBlock = ^{
NSLog(@"~~5~~%@", [NSThread currentThread]);
};
// 設(shè)置依賴(lài)
[op1 addDependency:op2];
[op1 addDependency:op3];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
}
3.線程間的通信
#pragma mark - NSOperation實(shí)現(xiàn)線程間的通信
- (void)NSOperationTest2 {
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://img.lanrentuku.com/img/allimg/1310/13822295641903.jpg"]];
UIImage *image = [UIImage imageWithData:data];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageview.image = image;
}];
}];
}
三、一些簡(jiǎn)單方法
// 取消單個(gè)操作
[operation cancel];
// 取消queue中所有的操作
[queue cancelAllOperations];
//設(shè)置隊(duì)列的最大并發(fā)操作數(shù)量
queue.maxConcurrentOperationCount = 1;
// 會(huì)阻塞當(dāng)前線程,等到某個(gè)operation執(zhí)行完畢
[operation waitUntilFinished];
// 阻塞當(dāng)前線程,等待queue的所有操作執(zhí)行完畢
[queue waitUntilAllOperationsAreFinished];
// 暫停queue YES代表暫停隊(duì)列,NO代表恢復(fù)隊(duì)列
[queue setSuspended:YES];
NSOperation 常用的基本上也就上面那些了。。。GCD 就不用多說(shuō)了,老東家蘋(píng)果推薦的,不過(guò)我卻覺(jué)得名字太長(zhǎng)~~~,一開(kāi)始總是記不住,多敲就好了!所有編程語(yǔ)言大概都有這么一個(gè)絕招O(∩_∩)O!
(四)iOS開(kāi)發(fā)之多線程 GCD
一、GCD 術(shù)語(yǔ)
要理解GCD ,我們先來(lái)熟悉與線程和并發(fā)相關(guān)的幾個(gè)概念。
1.Serial vs. Concurrent 串行 vs. 并發(fā)
任務(wù)串行執(zhí)行就是每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)并發(fā)執(zhí)行就是在同一時(shí)間可以有多個(gè)任務(wù)被執(zhí)行。
2.同步(Synchronous)與異步(Asynchronous)
同步,意味著在當(dāng)前線程中執(zhí)行任務(wù),不具備開(kāi)啟新的線程的能力。
異步,在新的線程中執(zhí)行任務(wù),具備開(kāi)啟新的線程的能力。
3.臨界區(qū)(Critical Section)
就是一段代碼不能被并發(fā)執(zhí)行,即兩個(gè)線程不能同時(shí)執(zhí)行這段代碼。
4.死鎖(Deadlock)
停止等待事情的線程會(huì)導(dǎo)致多個(gè)線程相互維持等待,即死鎖。
5.Thread Safe 線程安全
線程安全的代碼能在多線程或并發(fā)任務(wù)中被安全的調(diào)用,而不會(huì)導(dǎo)致任何問(wèn)題(數(shù)據(jù)損壞,崩潰等)。
6.Queues 隊(duì)列
GCD 提供有 dispatch queues 來(lái)處理代碼塊,這些隊(duì)列管理你提供給 GCD 的任務(wù)并用 FIFO 順序執(zhí)行這些任務(wù)。
二、代碼演示
1、dispatch_async(異步函數(shù))
//異步函數(shù) + 并發(fā)隊(duì)列:可以同時(shí)開(kāi)啟多條線程,任務(wù)是并行的
// 1.創(chuàng)建并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//2.將任務(wù)加入隊(duì)列
dispatch_async(queue, ^{
for (NSInteger i = 0 ; i < 5; i ++) {
NSLog(@"%ld~~~~1~~~~%@", i, [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"%ld~~~2~~~%@", i, [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"%ld~~~~3~~~~%@", i, [NSThread currentThread]);
}
});
//異步函數(shù) + 串行隊(duì)列:會(huì)開(kāi)啟新的線程,但是任務(wù)是串行的,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
// 1.創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("YMWM", DISPATCH_QUEUE_SERIAL);
// 2.將任務(wù)加入隊(duì)列
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5 ; i ++) {
NSLog(@"1~~~%@~~~~~%ld", [NSThread currentThread], i);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"2~~~%@~~~~~%ld", [NSThread currentThread], i);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"2~~~%@~~~~~%ld", [NSThread currentThread], i);
}
});
2、dispatch_sync(同步函數(shù))
//同步函數(shù) + 并發(fā)隊(duì)列:不會(huì)開(kāi)啟新的線程
// 1.獲得全局的并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.將任務(wù)加入隊(duì)列
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"1~~~%@~~~~~%ld", [NSThread currentThread], i);
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"2~~~%@~~~~~%ld", [NSThread currentThread], i);
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"3~~~%@~~~~~%ld", [NSThread currentThread], i);
}
});
//同步函數(shù) + 串行隊(duì)列:不會(huì)開(kāi)啟新的線程
// 1.創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("YMWM", DISPATCH_QUEUE_SERIAL);
// 2.將任務(wù)加入隊(duì)列
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"1~~~%@~~~~~%ld", [NSThread currentThread], i);
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"2~~~%@~~~~~%ld", [NSThread currentThread], i);
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"3~~~%@~~~~~%ld", [NSThread currentThread], i);
}
});
3.兩者在主隊(duì)列中的應(yīng)用
//異步函數(shù) + 主隊(duì)列:只在主線程中執(zhí)行任務(wù)
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"1~~~%@~~~~~%ld", [NSThread currentThread], i);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"2~~~%@~~~~~%ld", [NSThread currentThread], i);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"3~~~%@~~~~~%ld", [NSThread currentThread], i);
}
});
//同步函數(shù) + 主隊(duì)列:死循環(huán)
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"1~~~%@~~~~~%ld", [NSThread currentThread], i);
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"1~~~%@~~~~~%ld", [NSThread currentThread], i);
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
NSLog(@"1~~~%@~~~~~%ld", [NSThread currentThread], i);
}
});
4、dispatch_group_async的使用
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 創(chuàng)建一個(gè)隊(duì)列組
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
//添加操作...
[NSThread sleepForTimeInterval:1];
NSLog(@"1%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
//添加操作...
[NSThread sleepForTimeInterval:1];
NSLog(@"2%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
//添加操作...
[NSThread sleepForTimeInterval:1];
NSLog(@"3%@", [NSThread currentThread]);
});
// 回到主線程
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"回到主線程刷新UI");
});
5、dispatch_barrier_async的使用
dispatch_queue_t queue = dispatch_queue_create("YMWM", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:.5];
NSLog(@"1%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:.5];
NSLog(@"2%@", [NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"barrier --- %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:.5];
});
dispatch_async(queue, ^{
NSLog(@"3%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"4%@", [NSThread currentThread]);
});
6、dispatch_apply的使用
//執(zhí)行某個(gè)代碼片段10次
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"%@", [NSThread currentThread]);
});
7、dispatch_once_t的使用
第一個(gè)參數(shù)predicate,該參數(shù)是檢查后面第二個(gè)參數(shù)所代表的代碼塊是否被調(diào)用的謂詞,第二個(gè)參數(shù)則是在整個(gè)應(yīng)用程序中只會(huì)被調(diào)用一次的代碼塊
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"%@", [NSThread currentThread]);
});
8.dispatch_after的使用
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"-------");
//延遲執(zhí)行,較為精確
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"~~~~~~");
});
}
9.線程間的通信
#pragma mark - GCD-線程間的通信
- (void)downloadImage {
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://img.lanrentuku.com/img/allimg/1310/13822295641903.jpg"]];
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
self.imageview.image = image;
});
}
10.文件剪切
//第一種方式
//將文件從from剪切至to
NSString *from = @"/Users/iOS/Desktop/Test";
NSString *to = @"/Users/iOS/Desktop/test1";
NSFileManager *mgr = [NSFileManager defaultManager];
NSArray *subpaths = [mgr subpathsAtPath:from];
for (NSString *subpath in subpaths) {
NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//剪切
[mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
});
}
//第二種方式
//將文件從from剪切至to
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSString *from = @"/Users/iOS/Desktop/Test";
NSString *to = @"/Users/iOS/Desktop/test1";
NSFileManager *mag = [NSFileManager defaultManager];
NSArray *subpaths = [mag subpathsAtPath:from];
dispatch_apply(subpaths.count, queue, ^(size_t index) {
NSString *subpath = subpaths[index];
NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
// 剪切
[mag moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
NSLog(@"%@~~~~~~%@", [NSThread currentThread], subpath);
});
三、GCD與非GCD實(shí)現(xiàn)單粒設(shè)計(jì)模式
1.GCD實(shí)現(xiàn)設(shè)計(jì)模式
類(lèi)的.h文件
@interface PKPerson : NSObject
+ (instancetype)sharedCar;
@end
類(lèi)的.m文件
@interface PKPerson() <NSCopying>
@end
@implementation PKPerson
// 實(shí)例變量,當(dāng)前類(lèi)
static id _instance;
// 重寫(xiě)allocWithZone方法
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
static dispatch_once_t onceToken;
// 該方法整個(gè)應(yīng)用程序只調(diào)用一次
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
+ (instancetype)sharedInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
// 保證每復(fù)制一個(gè)對(duì)象也是同一內(nèi)存空間
- (id)copyWithZone:(NSZone *)zone{
return _instance;
}
@end
2.宏定義封裝GCD單粒設(shè)計(jì)模式
通常當(dāng)多個(gè)類(lèi)之間有相同的屬性和方法時(shí),我們會(huì)考慮將相同的部分抽取出來(lái),封裝到父類(lèi)中,但單粒模式不可以這樣做,因?yàn)閱瘟DJ讲捎美^承方式的話,所有的類(lèi)會(huì)共享一份內(nèi)存。所以一般采取宏定義封裝GCD單粒設(shè)計(jì)模式。注意,每行的結(jié)尾需要添加 ""。
// .h文件
#define PKSingletonH + (instancetype)sharedInstance;
// .m文件
#define PKSingletonH \
static id _instace; \
\
+ (instancetype)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super allocWithZone:zone]; \
}); \
return _instace; \
} \
\
+ (instancetype)sharedInstance \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [[self alloc] init]; \
}); \
return _instace; \
} \
\
- (id)copyWithZone:(NSZone *)zone \
{ \
return _instace; \
}
3.非GCD實(shí)現(xiàn)設(shè)計(jì)模式
類(lèi)的.h文件
@interface PKPerson : NSObject
+ (instancetype)sharedInstance;
@end
類(lèi)的.m文件
#import "PKPerson.h"
@interface PKPerson()
@end
@implementation PKPerson
static id _instance;
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
// 同步鎖,防止多線程同時(shí)進(jìn)入
@synchronized(self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
+ (instancetype)sharedInstance
{
@synchronized(self) {
if (_instance == nil) {
_instance = [[self alloc] init];
}
}
return _instance;
}
- (id)copyWithZone:(NSZone *)zone
{
return _instance;
}
@end
四、GCD定時(shí)器
@interface ViewController ()
/** 定時(shí)器(這里不用帶*,因?yàn)閐ispatch_source_t就是個(gè)類(lèi),內(nèi)部已經(jīng)包含了*) */
@property (nonatomic, strong) dispatch_source_t timer;
@end
@implementation ViewController
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 獲得隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
// 創(chuàng)建一個(gè)定時(shí)器(dispatch_source_t本質(zhì)還是個(gè)OC對(duì)象)
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// GCD的時(shí)間參數(shù)一般是納秒(1秒 == 10的9次方納秒)
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC);
dispatch_source_set_timer(self.timer, start, interval, 0);
// 設(shè)置回調(diào)
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"------%@-------", [NSThread currentThread]);
});
// 啟動(dòng)定時(shí)器
dispatch_resume(self.timer);
}
三種多線程編程都說(shuō)完了,代碼有些多,不過(guò)希望朋友們有所收獲,另外再補(bǔ)充一些關(guān)于線程間通信的。。。
(五)iOS開(kāi)發(fā)之多線程 線程間的通信
一、簡(jiǎn)單介紹
在一個(gè)進(jìn)程中,線程往往不是孤立存在的,多個(gè)線程之間需要經(jīng)常進(jìn)行通信。以下是線程之間進(jìn)行通信的方法:
二、常用方法:
代碼1:performSelector
performSelector常用方法的常用方法主要有以下幾種:
//在主線程上執(zhí)行操作
[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES];
//在當(dāng)前線程執(zhí)行操作
[self performSelector:@selector(run) withObject:nil];
//在指定線程上執(zhí)行操作
[self performSelector:@selector(run) onThread:thread withObject:nil waitUntilDone:YES];
具體代碼:
//點(diǎn)擊屏幕開(kāi)始執(zhí)行以下方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self performSelectorInBackground:@selector(download) withObject:nil];
}
- (void)download
{
// 圖片的網(wǎng)絡(luò)路徑
NSURL *url = [NSURL URLWithString:@"http://img.lanrentuku.com/img/allimg/1310/13822295641903.jpg"];
// 根據(jù)地址加載圖片
NSData *data = [NSData dataWithContentsOfURL:url]; //耗時(shí)操作
// 生成圖片
UIImage *image = [UIImage imageWithData:data];
// 回到主線程,刷新UI界面
//方式一
[self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
//方式二
// [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
//方式三
// [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:NO];
}
//顯示圖片(如果performSelector調(diào)用的是setImage:方法,會(huì)直接調(diào)用系統(tǒng)的方法)
//- (void)showImage:(UIImage *)image
//{
// self.imageView.image = image;
//}
代碼2:NSOperationQueue方式
一個(gè)NSOperation對(duì)象可以通過(guò)調(diào)用start方法來(lái)執(zhí)行任務(wù),默認(rèn)是同步執(zhí)行的.也可以將NSOperation添加到一個(gè)NSOperationQueue中去執(zhí)行,而且是異步執(zhí)行的.
#pragma mark - NSOperation實(shí)現(xiàn)線程間的通信
- (void)NSOperationTest {
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://img.lanrentuku.com/img/allimg/1310/13822295641903.jpg"]];
UIImage *image = [UIImage imageWithData:data];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
}
代碼3:GCD方式
GCD,全稱(chēng)Grand Central Dispath,是蘋(píng)果開(kāi)發(fā)的一種支持并行操作的機(jī)制。它的主要部件是一個(gè)FIFO隊(duì)列和一個(gè)線程池,前者用來(lái)添加任務(wù),后者用來(lái)執(zhí)行任務(wù)。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 圖片的網(wǎng)絡(luò)路徑
NSURL *url = [NSURL URLWithString:@"http://img.lanrentuku.com/img/allimg/1310/13822295641903.jpg"];
// 加載圖片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成圖片
UIImage *image = [UIImage imageWithData:data];
// 回到主線程顯示圖片
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
三、NSPort方法:
NSPort有3個(gè)子類(lèi),NSSocketPort、NSMessagePort、NSMachPort,但在iOS下只有NSMachPort可用。代碼如下:
UIViewController中的代碼:
#import "ViewController.h"
#import "MyPort.h"
@interface ViewController () <NSPortDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//創(chuàng)建主線程的port
NSPort *myPort = [NSPort port];
//設(shè)置代理
myPort.delegate = self;
//把port加入runloop
[[NSRunLoop currentRunLoop] addPort:myPort forMode:NSDefaultRunLoopMode];
//啟動(dòng)子線程,并傳入主線程的port
MyPort *work = [[MyPort alloc] init];
[NSThread detachNewThreadSelector:@selector(launchThreadWithPort:)
toTarget:work
withObject:myPort];
}
- (void)handlePortMessage:(NSMessagePort*)message{
NSLog(@"已接到子線程的消息 --- %@", message);
}
@end
MyPort中的代碼:
.h文件:
#import <Foundation/Foundation.h>
@interface MyPort : NSPort
@end
.m文件:
#import "MyPort.h"
#define PKMessage 10
@interface MyPort ()<NSMachPortDelegate> {
NSPort *firstPort;
NSPort *secondPort;
}
@end
@implementation MyPort
- (void)launchThreadWithPort:(NSPort *)port {
@autoreleasepool {
//接收主線程傳入的port
//設(shè)置子線程名字
[[NSThread currentThread] setName:@"MyThread"];
//開(kāi)啟runloop
[[NSRunLoop currentRunLoop] run];
//創(chuàng)建自己port
secondPort = [NSPort port];
secondPort.delegate = self;
//將自己的port添加到runloop,防止runloop執(zhí)行完畢之后退出,接收主線程發(fā)送過(guò)來(lái)的port消息
[[NSRunLoop currentRunLoop] addPort:secondPort forMode:NSDefaultRunLoopMode];
//完成向主線程發(fā)送消息
[self sendPortMessage];
}
}
/**
* 完成向主線程發(fā)送消息
*/
- (void)sendPortMessage {
//發(fā)送消息到主線程
[firstPort sendBeforeDate:[NSDate date]
msgid:PKMessage
components:nil
from:secondPort
reserved:0];
}
提示:通常來(lái)說(shuō), NSPort可以做的事情通過(guò)performSelector也同樣可以搞定,因此線程間通信完全可以通過(guò)performSelector來(lái)實(shí)現(xiàn)。
<end>
結(jié)束語(yǔ):以上是多線程相關(guān)的知識(shí)總結(jié),本人也是小白級(jí)別,如果有寫(xiě)錯(cuò)的地方,還請(qǐng)朋友們能夠指出來(lái),謝謝!