單例模式:保證一個類僅有一個實體,并提供一個訪問它的全域變數點,
在OC中,構建單例模式較簡單的是提供一個類方法getInstance來進行構造和訪問,
#import "Count.h"
@implementation Count
static Count* _instance=nil;
+(instancetype)getInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance=[Count new];
});
return _instance;
}
@end
首先宣告一個全域的Count類物件_instance,作為單例, 類方法中使用了GCD中的dispatch_once方法,這里涉及到了多執行緒,使用 dispatch_once 方法能保證某段代碼在程式運行程序中只被執行 1 次,并且即使在多執行緒的環境下,dispatch_once 也可以保證執行緒安全,
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance=[Count new];
});
但這樣的話,使用構造方法和使用getInstance類方法構造的物件并不是同一個,也就是這個類初始化了兩次,如下圖,

所以,我們需要重寫一下alloc方法,來保證Count類只初始化了一次,
+(instancetype)alloc{
if(_instance){
return _instance;
}
return [super alloc];
}
然后我們再運行 看看使用構造方法和類方法的情況
這里我們可以看到c1和c2的地址相同,說明Count類只初始化了一次,
同時為了避免copy和mutablecopy,我們可以選擇重寫或者禁用這兩個方法,
附上完整代碼:
#import "Count.h"
@implementation Count
static Count* _instance=nil;
+(instancetype)alloc{
if(_instance){
return _instance;
}
return [super alloc];
}
+(instancetype)getInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance=[Count new];
});
return _instance;
}
@end
GCD
前面在構造單例的時候,使用了GCD的dispatch_once 方法,現在說一下GCD
首先,什么是GCD?
GCD (Grand Central Dispatch)
它主要用于優化應用程式以支持多核處理器以及其他對稱多處理系統,它是一個在執行緒池模式的基礎上執行的并行任務,在Mac OS X 10.6雪豹中首次推出,也可在IOS 4及以上版本使用,
使用GCD有哪些好處?
- GCD 可用于多核的并行運算;
- GCD 會自動利用更多的 CPU 內核(比如雙核、四核);
- GCD 會自動管理執行緒的生命周期(創建執行緒、調度任務、銷毀執行緒);
- 程式員只需要告訴 GCD 想要執行什么任務,不需要撰寫任何執行緒管理代碼,
在GCD中有兩個重要的概念:任務 和 佇列
任務:就是要執行的代碼塊,在block中的代碼,
佇列:就是存放任務的一個佇列,具有資料結構中佇列的性質,先進先出,
任務執行方式有兩種:同步執行 和 異步執行
- 同步執行(sync):
- 同步添加任務到指定的佇列中,在添加的任務執行結束之前,會一直等待,直到佇列里面的任務完成之后再繼續執行,
- 只能在當前執行緒中執行任務,不具備開啟新執行緒的能力,
- 異步執行(async):
- 異步添加任務到指定的佇列中,它不會做任何等待,可以繼續執行任務,
- 可以在新的執行緒中執行任務,具備開啟新執行緒的能力,
佇列也有兩種:串行佇列 和 并發佇列
- 串行佇列(Serial Dispatch Queue):
- 每次只有一個任務被執行,讓任務一個接著一個地執行,(只開啟一個執行緒,一個任務執行完畢后,再執行下一個任務)
- 并發佇列(Concurrent Dispatch Queue):
- 可以讓多個任務并發(同時)執行,(可以開啟多個執行緒,并且同時執行任務)
注意:并發佇列的并發功能只有在異步(dispatch_async)方法下才有效,
兩者的具體區別為:

GCD的使用
GCD 的使用步驟其實很簡單,只有兩步:
- 創建一個佇列(串行佇列或并發佇列);
- 將任務追加到任務的等待佇列中,然后系統就會根據任務型別執行任務
可以使用dispatch_queue_create來創建佇列,該方法有兩個引數,第一個引數為識別符號,可填可不填, 第二個為所創建的佇列型別 DISPATCH_QUEUE_SERIAL為串行佇列 DISPATCH_QUEUE_CONCURRENT為并行佇列,
//創建串行佇列
dispatch_queue_t queue= dispatch_queue_create("test1", DISPATCH_QUEUE_SERIAL);
//創建并行佇列
dispatch_queue_t queue=dispatch_queue_create("test2", DISPATCH_QUEUE_CONCURRENT);
GCD還提供了主佇列,主佇列為串行佇列,使用dispatch_get_main_queue()得到主佇列
所有放到主佇列中的任務都會放到主執行緒中執行,
//主佇列的獲取方法
dispatch_queue_t mainQueue=dispatch_get_main_queue();
GCD還有一個全域佇列,為并發佇列,使用dispatch_get_global_queue()得到,
dispatch_queue_t queue=dispatch_get_global_queue(<#long identifier#>, <#unsigned long flags#>)
第一個引數為佇列的優先級,一般為DISPATCH_QUEUE_PRIORITY_DEFAULT,第二個引數一般為0;
例子分析:
首先添加兩個方法
-(void)startCount{
dispatch_queue_t queue= dispatch_queue_create("test1", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"this is first");
NSLog(@"%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"this is second");
NSLog(@"%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"this is third");
NSLog(@"%@",[NSThread currentThread]);
});
}
-(void)asyncCount{
dispatch_queue_t queue=dispatch_queue_create("test2", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
//NSLog(@"this is the first in context");
//[NSThread sleepForTimeInterval:2];
NSLog(@"%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
//NSLog(@"this is the second in context");
//[NSThread sleepForTimeInterval:2];
NSLog(@"%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
//NSLog(@"this is the third in context");
//[NSThread sleepForTimeInterval:2];
NSLog(@"%@",[NSThread currentThread]);
});
}
startCount創建了一個串行佇列,并且同步執行了三個任務
asyncCount創建了一個并發佇列,并且異步執行了三個任務
我們運行一下程式,執行兩個方法
#import <Foundation/Foundation.h>
#import "Count.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Count* c1=[[Count alloc]init];
[c1 asyncCount];
[c1 startCount];
}
return 0;
}
2021-09-27 01:42:17.550509-0700 GCD[3337:37060] this is first
2021-09-27 01:42:17.550834-0700 GCD[3337:37085] <NSThread: 0x1006007c0>{number = 2, name = (null)}
2021-09-27 01:42:17.550915-0700 GCD[3337:37086] <NSThread: 0x100700210>{number = 3, name = (null)}
2021-09-27 01:42:17.550931-0700 GCD[3337:37087] <NSThread: 0x100505070>{number = 4, name = (null)}
2021-09-27 01:42:17.550942-0700 GCD[3337:37060] <NSThread: 0x10050b280>{number = 1, name = main}
2021-09-27 01:42:17.550956-0700 GCD[3337:37060] this is second
2021-09-27 01:42:17.550973-0700 GCD[3337:37060] <NSThread: 0x10050b280>{number = 1, name = main}
2021-09-27 01:42:17.550980-0700 GCD[3337:37060] this is third
2021-09-27 01:42:17.550989-0700 GCD[3337:37060] <NSThread: 0x10050b280>{number = 1, name = main}
Program ended with exit code: 0
首先,我們知道利用NSThread的currentThread得到的是當前執行緒,即上面的number表示的為第幾個執行緒,
我們打個斷點進行分析,可以通過Xcode看到下圖
顯示了我們創建的佇列test1和test2,并且異步執行并發佇列中的三個任務創建了三個執行緒
我們來看輸出結果
第一句
2021-09-27 01:42:17.550509-0700 GCD[3337:37060] this is first
這個是第二個方法,startCount的第一個輸出,屬于執行緒1,我們知道同步執行是無法開辟執行緒的,所以只有Thread1存在,
為什么第二個方法反而會比第一個asyncCount先輸出結果?
因為asyncCount中為異步執行,不需要等它結束再去運行下一部分,將任務添加到佇列后,無需等待,就開始運行startCount方法,這時才輸出this is first;
同時,我們注意到startCount創建的佇列在的執行緒為1,所以能判斷是startCount先執行任務,創建執行緒,
第二句-第四句
2021-09-27 01:56:09.143174-0700 GCD[3766:41073] <NSThread: 0x100687a60>{number = 2, name = (null)}
2021-09-27 01:56:09.143180-0700 GCD[3766:41075] <NSThread: 0x102a003b0>{number = 3, name = (null)}
2021-09-27 01:56:09.143180-0700 GCD[3766:41074] <NSThread: 0x1005073c0>{number = 4, name = (null)}
我們可以看到這是asyncCount方法的輸出結果,輸出了三個任務所在的三個執行緒,并且是按順序輸出的
也就是說asyncCount開辟了三個執行緒,為每一個任務開辟了一個執行緒
當我再運行一次,輸出結果的這三行為:
2021-09-27 01:56:09.143174-0700 GCD[3766:41073] <NSThread: 0x100687a60>{number = 2, name = (null)}
2021-09-27 01:56:09.143180-0700 GCD[3766:41075] <NSThread: 0x102a003b0>{number = 4, name = (null)}
2021-09-27 01:56:09.143180-0700 GCD[3766:41074] <NSThread: 0x1005073c0>{number = 3, name = (null)}
但這三個執行緒并不是按順序輸出的,Thread4比Thread3要先輸出,為什么?
按我個人的理解為,Thread3先創建,Thread4后創建,但是在搶占資源時,Thread4要比Thread3搶占了更多的資源,先結束,
為了驗證,我又多次運行程式,可以得到這三行的順序是不定的,也就可以看到各個執行緒搶占資源的情況還是隨機的
第五句-第九句
2021-09-27 02:02:12.252049-0700 GCD[3957:42833] <NSThread: 0x10050b280>{number = 1, name = main}
2021-09-27 02:02:12.252065-0700 GCD[3957:42833] this is second
2021-09-27 02:02:12.252083-0700 GCD[3957:42833] <NSThread: 0x10050b280>{number = 1, name = main}
2021-09-27 02:02:12.252093-0700 GCD[3957:42833] this is third
2021-09-27 02:02:12.252127-0700 GCD[3957:42833] <NSThread: 0x10050b280>{number = 1, name = main}
為startCount的結果,這時,Thread2,Thread3,Thread4執行緒都結束了,只有Thread1在運行,從結果可以看出Thread1中的三個任務在同一個執行緒,
但是通過上面的分析得到各個執行緒搶占資源是隨機的,所以Thread1也應該如此,然后,在嘗試多次后,出現了各個執行緒比較混亂的情況
2021-09-27 02:07:28.992934-0700 GCD[4121:44166] this is first
2021-09-27 02:07:28.993211-0700 GCD[4121:44179] <NSThread: 0x10200a400>{number = 3, name = (null)}
2021-09-27 02:07:28.993228-0700 GCD[4121:44166] <NSThread: 0x1021035e0>{number = 1, name = main}
2021-09-27 02:07:28.993243-0700 GCD[4121:44166] this is second
2021-09-27 02:07:28.993257-0700 GCD[4121:44166] <NSThread: 0x1021035e0>{number = 1, name = main}
2021-09-27 02:07:28.993265-0700 GCD[4121:44166] this is third
2021-09-27 02:07:28.993273-0700 GCD[4121:44180] <NSThread: 0x1020001f0>{number = 4, name = (null)}
2021-09-27 02:07:28.993275-0700 GCD[4121:44166] <NSThread: 0x1021035e0>{number = 1, name = main}
2021-09-27 02:07:28.993273-0700 GCD[4121:44178] <NSThread: 0x100583da0>{number = 2, name = (null)}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/303615.html
標籤:其他
上一篇:常見微處理器體系架構
