1.什么是進程刁赖?
進程是指在一個系統(tǒng)內(nèi)正在運行的一個應(yīng)用程序揍拆。每個進程之間是獨立的芽淡,每個進程均運行在其專用且受保護的內(nèi)存空間內(nèi)。比如同時打開兩個應(yīng)用支付寶和微信择示,系統(tǒng)就會分別啟動2個進程束凑。
2.什么是線程?
1個進程如果想要執(zhí)行任務(wù)栅盲,必須要有線程(每一個進程至少要有一個線程)
線程是進程的基本執(zhí)行單元汪诉,一個進程(程序)的所有任務(wù)必須要在線程中執(zhí)行
3.線程的串行
一個線程中的任務(wù)的執(zhí)行是串行的,如果要在一個線程中執(zhí)行多個任務(wù)谈秫,只能一個一個地桉順序執(zhí)行扒寄;換句話說,在同一時間內(nèi)孝常,一個線程只能執(zhí)行一個任務(wù)旗们。
4.什么是多線程
一個進程中可以開啟多條線程,每條線程并行(同時)執(zhí)行不同的任務(wù)
優(yōu)點:多線程可以提供程序的執(zhí)行效率
5.多線程的原理
同一時間构灸,CPU只能處理一條線程上渴,只有一條線程在工作(執(zhí)行),當多線程并發(fā)(同時)執(zhí)行喜颁,實質(zhì)也就是CPU快速地在多條線程之間調(diào)度(切換)
如果CPU調(diào)度線程的時間足夠快稠氮,就會造成了多線程并發(fā)執(zhí)行的假象。如果線程非常的多半开,CPU將會在N多線程之間調(diào)度隔披,CPU會被活活累死,消耗大量的CPU資源寂拆,這樣也就導(dǎo)致了每條線程被調(diào)度執(zhí)行的頻次會降低奢米,即線程的執(zhí)行效率降低
6.多線程的優(yōu)點:
不僅能適當提高程序的執(zhí)行效率抓韩,還能提高資源利用率,如CPU鬓长、內(nèi)存的利用率
7.多線程的缺點:
開啟多線程需要占用一定的內(nèi)存空間:默認情況下谒拴,主線程占用1M,子線程占用512KB涉波,如果開啟大量的線程英上,或占用大量的內(nèi)存空間,降低程序的性能啤覆。如果線程的數(shù)量很多苍日,不止會使CPU在調(diào)度線程上的開銷增大,也會使得程序設(shè)計更加復(fù)雜:如線程間的通信和數(shù)據(jù)共享等窗声。
8.多線程的應(yīng)用:
主線程:一個iOS程序運行后相恃,默認會開啟一條線程,這條線程就被稱為"主線程"和"UI線程"
主線程的作用:顯示\刷新UI界面以及處理UI事件(如:點擊事件笨觅、滾動事件豆茫、拖拽事件等)
主線程的使用注意事項:不要把比較耗時的操作放到主線程中。因為耗時的操作會卡住主線程屋摇,對UI的流暢度造成嚴重的影響,給用戶一種"卡"的壞體驗
//8.1多線程的安全隱患
//8.1.1資源共享:
一塊資源可能會被多個線程共享幽邓,也就是多個線程可能會訪問同一塊資源炮温。如多個線程訪問同一個對象或者同一個變量,再或者是同一個文件牵舵。當多個線程訪問同一塊資源時柒啤,很容易造成引發(fā)數(shù)據(jù)錯亂和數(shù)據(jù)安全的問題
8.1.2-----解決辦法
互斥鎖使用格式---
@synchronized (鎖對象) {
//需要鎖定的代碼
}
注意:鎖定一份代碼只用一把鎖,用多把鎖是無效的
互斥鎖的優(yōu)缺點:
優(yōu)點:能有效防止因多線程搶奪資源造成的數(shù)據(jù)安全問題
缺點:需要消耗大量的CPU資源
互斥鎖使用的前提是:多條線程去搶奪同一塊資源
9.創(chuàng)建線程
1.一個NSThread對象就代表一條線程畸颅,
1.1->創(chuàng)建和啟動線程
//創(chuàng)建線程
NSThread* thread = [[NSThread alloc]initWithTarget:self selector:@selector(initThread) object:nil];
//啟動線程
[thread start];
//特點:線程一啟動担巩,就會在線程thread中執(zhí)行self的initThread方法
//2--主線程的相關(guān)用法:
//2.1-------------是否為主線程
//? ? @property (readonly) BOOL isMainThread NS_AVAILABLE(10_5, 2_0);
//thread.isMainThread
//2.2-------------是否為主線程
//? ? @property (class, readonly) BOOL isMainThread NS_AVAILABLE(10_5, 2_0); // reports whether current thread is main
//屬性用法? ? [NSThread isMainThread];
//2.3-------------獲得主線程
//? ? @property (class, readonly, strong) NSThread *mainThread NS_AVAILABLE(10_5, 2_0);
//屬性用法[NSThread mainThread];
//2.4---線程的調(diào)度優(yōu)先級:調(diào)度優(yōu)先級的取值范圍是0.0~1.0,默認0.5没炒,值越大涛癌,優(yōu)先級越高
//? ? + (double)threadPriority;
//? ? + (BOOL)setThreadPriority:(double)p;
//設(shè)置線程的名字
// 2.5---? @property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
//3-----其他創(chuàng)建線程的方式
//3.1----創(chuàng)建線程后自動啟動線程
//+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
//3.2----隱式創(chuàng)建并啟動線程
//? ? [self performSelectorInBackground:@selector(initThread) withObject:nil];
//上述2種創(chuàng)建線程的優(yōu)缺點:優(yōu)點是簡單快捷,缺點是無法對線程進行更詳細的設(shè)置送火。
當前流程繼續(xù)performSelector關(guān)于內(nèi)存管理的執(zhí)行原理是這樣的執(zhí)行 [self performSelector:@selector(method1:) withObject:self.tableLayer afterDelay:3]; 的時候拳话,系統(tǒng)會將tableLayer的引用計數(shù)加1,執(zhí)行完這個方法時种吸,還會將tableLayer的引用計數(shù)減1弃衍,由于延遲這時tableLayer的引用計數(shù)沒有減少到0,也就導(dǎo)致了切換場景dealloc方法沒有被調(diào)用坚俗,出現(xiàn)了內(nèi)存泄露镜盯。
利用如下函數(shù):
[NSObject cancelPreviousPerformRequestsWithTarget:self]
當然你也可以一個一個得這樣用:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(method1:) object:nil]
加上了這個以后岸裙,順利地執(zhí)行了dealloc方法
4. 線程間的通信
4.1---線程間通信的含義:在一個進程中,線程往往不是孤立存在的速缆,多個線程之間需要經(jīng)常進行通信
4.2---線程間通信的體現(xiàn):一個線程給另一個線程傳遞數(shù)據(jù)或者一個線程執(zhí)行完之后去執(zhí)行另一個線程
4.3---- 線程間通信的常用方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
4.4---從子線程回到主線程
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)?耗時的異步操作...
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主線程,執(zhí)?UI刷新操作
});
});
或者
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)?耗時的異步操作...
// 回到主線程,執(zhí)?UI刷新操作
[self.imageView performSelectorOnMainThread:@selector(方法) withObject:image waitUntilDone:NO];
});
5.GCD:Grand Central Dispatch
5.1--GCD是純C語言降允,提供了非常強大的函數(shù)
5.2---GCD的優(yōu)勢:
GCD是蘋果公司為多核的并行運算提出的解決方案
GCD會自動利用更多的CPU內(nèi)核(比如雙核、四核)
GCD會自動管理線程的生命周期(創(chuàng)建線程激涤、調(diào)度任務(wù)拟糕、銷毀線程)
程序員只需要告訴GCD想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼
5.3-------
GCD的兩個核心概念:
5.3.1--任務(wù):執(zhí)行什么操作
5.3.2--隊列:用來存放任務(wù)
GCD的使用就兩個步驟:
1->定制任務(wù)
2->確定想要做的事情
將任務(wù)添加到隊列中倦踢,GCD會自動將隊列中的任務(wù)取出送滞,放到對應(yīng)的線程中執(zhí)行
重點:任務(wù)的取出遵循隊列的FIFO原則:先進先出,后進后出
5.4---執(zhí)行任務(wù)
5.4.1----GCD中有兩個用來執(zhí)行任務(wù)的函數(shù)辱挥。特別說明:把右邊的參數(shù)(任務(wù)-<#^(void)block#>)提交給左邊的參數(shù)(隊列:<#dispatch_queue_t? _Nonnull queue#>)進行執(zhí)行
第一種:用同步的方式執(zhí)行任務(wù)? ? dispatch_sync(<#dispatch_queue_t? _Nonnull queue#>, <#^(void)block#>)
參數(shù)說明:queue:隊列? ? ? ? block:任務(wù)
第二種:用異步的方式執(zhí)行任務(wù)dispatch_async(<#dispatch_queue_t? _Nonnull queue#>, <#^(void)block#>)
5.4.2---同步和異步的區(qū)別
同步:在當前線程中執(zhí)行任務(wù)犁嗅,其并不具備開啟新線程的能力
異步:在另一條線程中執(zhí)行任務(wù),它具備了開啟新線程的能力
5.5---隊列
GCD的隊列類型可以分為兩種類型:
1->并發(fā)隊列:可以讓多個任務(wù)并發(fā)(同時)執(zhí)行(會自動開啟多個線程同時執(zhí)行任務(wù))晤碘,并發(fā)功能只有在異步:dispatch_async函數(shù)下才是有效的
GCD默認已經(jīng)提供了全局的并發(fā)隊列褂微,供整個應(yīng)用使用,不需要手動創(chuàng)建
使用dispatch_get_global_queue函數(shù)獲得全局的并發(fā)隊列
dispatch_queue_t dispatch_get_global_queue(dispatch_queue_priority_t priority,unsigned long flags); // 此參數(shù)暫時無用园爷,用0即可
示例:
這個參數(shù)是留給以后用的宠蚂,暫時用不上,傳個0童社。
第一個參數(shù)為優(yōu)先級求厕,這里選擇默認的。獲取一個全局的默認優(yōu)先級的并發(fā)隊列扰楼。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 獲得全局并發(fā)隊列
說明:全局并發(fā)隊列的優(yōu)先級
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默認(中)
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后臺
2->串行隊列:讓任務(wù)一個接著一個的執(zhí)行呀癣,即一個任務(wù)執(zhí)行完了,再去執(zhí)行另一個任務(wù)
GCD中獲得串行有兩種方法:
1.1->使用dispatch_queue_create函數(shù)創(chuàng)建串行隊列
dispatch_queue_t? dispatch_queue_create(const char *label,? dispatch_queue_attr_t attr); // 隊列名稱弦赖, 隊列屬性项栏,一般用NULL即可
隊列名稱的作用:
將來調(diào)試的時候,可以看得出任務(wù)是在哪個隊列中執(zhí)行的蹬竖。
示例:
dispatch_queue_t queue = dispatch_queue_create("wendingding", NULL); // 創(chuàng)建
dispatch_release(queue); // 非ARC需要釋放手動創(chuàng)建的隊列
1.2-> 使用主隊列(跟主線程相關(guān)聯(lián)的隊列)
主隊列是GCD自帶的一種特殊的串行隊列,放在主隊列中的任務(wù)沼沈,都會放到主線程中執(zhí)行
使用dispatch_get_main_queue()獲得主隊列
示例:dispatch_queue_t queue = dispatch_get_main_queue();
小結(jié)
說明:同步函數(shù)不具備開啟線程的能力,無論是什么隊列都不會開啟線程币厕;異步函數(shù)具備開啟線程的能力庆冕,開啟幾條線程由隊列決定(串行隊列只會開啟一條新的線程,并發(fā)隊列會開啟多條線程)劈榨。
//iOS的延時操作
/*
iOS常見的延時操作有兩種方法:
1.調(diào)用NSObject的方法: [self performSelector:@selector(initThread) withObject:nil afterDelay:2.0];
2秒后調(diào)用self的initThread方法访递,該方法在那個線程調(diào)用,那么run就在哪個線程執(zhí)行(當前線程)同辣,通常是主線程拷姿。
2.使用GCD函數(shù)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//2秒后執(zhí)行這里
});
如果隊列是主隊列惭载,那么就在主線程執(zhí)行,如果隊列是并發(fā)隊列响巢,那么會新開啟一個線程描滔,在子線程中執(zhí)行。
/*
NSOperation
1.---- NSOperation的作用:配合使用NSOperation和NSOperationQueue也能實現(xiàn)多線程編程
其實現(xiàn)多線程的具體步驟:
1.1->將要執(zhí)行的操作封裝到一個NSOperation對象中
1.2->將NSOperation對象添加到NSOperationQueue中
1.3->系統(tǒng)會自動將NSOperationQueue中的NSOperation取出來
1.4->將取出的NSOperation封裝的操作放到一條新線程中執(zhí)行
2----NSOperation的子類
NSOperation是一個抽象類踪古,并不具備封裝操作的能力含长,一定要使用他的子類才可以
使用NSOperationi子類的方式有三種:
(1)NSInvocationOperation
(2)NSBlockOperation
(3)自定義子類繼承NSOperation,實現(xiàn)內(nèi)部相應(yīng)的?法
3.---創(chuàng)建操作對象,封裝要執(zhí)行的任務(wù)
3.1->NSInvocationOperation
NSInvocationOperation* operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(initThread) object:nil];
//執(zhí)行操作
[operation start];
//說明:一旦執(zhí)行操作伏穆,就會調(diào)用target的initThread方法
重點:操作對象默認在主線程中執(zhí)行拘泞,只有添加到隊列中才會開啟新的線程。即默認情況下枕扫,如果操作沒有放到隊列中queue中陪腌,都是同步執(zhí)行。只有將NSOperation放到一個NSOperationQueue中,才會異步執(zhí)行操作
3.2->--NSBlockOperation
//封裝操作
//創(chuàng)建NSBlockOperation操作對象
NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock:^{
}];
//添加操作
[operation addExecutionBlock:^{
}];
//開啟執(zhí)行操作
[operation start];
//重點:只要NSBlockOperation封裝的操作數(shù)大于一烟瞧,就會異步執(zhí)行操作
4.NSOperationQueue
NSOperationQueue的作用:NSOperation可以調(diào)用start方法來執(zhí)行任務(wù)诗鸭,但默認是同步執(zhí)行的,如果將NSOperation添加到NSOperationQueue(操作隊列)中,系統(tǒng)會自動異步執(zhí)行NSOperation中的操作参滴,自動開啟線程
NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock:^{
}];
//添加操作
[operation addExecutionBlock:^{
}];
//創(chuàng)建NSOperationQueue
NSOperationQueue* queue = [[NSOperationQueue alloc]init];
//把操作添加到隊列中
//第一種添加方式:
[queue addOperation:operation];
//第二種添加方式:
[queue addOperationWithBlock:^{
}];
重點:系統(tǒng)自動將NSOperationqueue中的NSOperation對象取出强岸,將其封裝的操作放到一條新的線程中執(zhí)行。
NSOperation基本操作
1.->并發(fā)數(shù):同時執(zhí)行的任務(wù)數(shù)砾赔,如果同時開啟3個線程執(zhí)行3個任務(wù)请唱,那并發(fā)數(shù)就是3了
2-> 最大并發(fā)數(shù):同一時間最多只能執(zhí)行的任務(wù)個數(shù)
3-> 最大并發(fā)數(shù)的相關(guān)方法
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
特別說明:如果沒有設(shè)置最大并發(fā)數(shù),那么并發(fā)的個數(shù)會有系統(tǒng)內(nèi)存和CPU決定过蹂,
溫馨提示:最大并發(fā)數(shù)不要亂寫,最好5以內(nèi)聚至,一般以2~3為宜酷勺,因為雖然任務(wù)是在子線程進行處理的,但是CPU處理這些過多的子線程可能會影響UI扳躬,讓UI變卡脆诉。
隊列的取消,暫停和恢復(fù)
1->取消隊列的所有操作
- (void)cancelAllOperations;
提?:也可以調(diào)用NSOperation的- (void)cancel?法取消單個操作
2->暫停和恢復(fù)隊列
- (void)setSuspended:(BOOL)b; // YES代表暫停隊列,NO代表恢復(fù)隊列
- (BOOL)isSuspended; //當前狀態(tài)
3->暫停和恢復(fù)的適用場合:在tableview界面贷币,開線程下載遠程的網(wǎng)絡(luò)界面击胜,對UI會有影響,使用戶體驗變差役纹。那么這種情況偶摔,就可以設(shè)置在用戶操作UI(如滾動屏幕)的時候,暫停隊列(不是取消隊列)促脉,停止?jié)L動的時候辰斋,恢復(fù)隊列策州。
操作優(yōu)先級
1->設(shè)置NSOperation在queue中的優(yōu)先級,可以改變操作的執(zhí)?優(yōu)先級
- (NSOperationQueuePriority)queuePriority;
- (void)setQueuePriority:(NSOperationQueuePriority)p;
(2)優(yōu)先級的取值
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
說明:優(yōu)先級高的任務(wù),調(diào)用的幾率會更大宫仗。
操作依賴
NSOperation之間可以設(shè)置依賴來保證執(zhí)行順序够挂,?如一定要讓操作A執(zhí)行完后,才能執(zhí)行操作B,可以像下面這么寫
[operationB addDependency:operationA]; // 操作B依賴于操作
但是不能循環(huán)依賴
重點:任務(wù)添加的順序并不能夠決定執(zhí)行順序,執(zhí)行的順序取決于依賴藕夫。使用Operation的目的就是為了讓開發(fā)人員不再關(guān)心線程孽糖。
操作的監(jiān)聽:
可以監(jiān)聽一個操作的執(zhí)行完畢
- (void (^)(void))completionBlock;
- (void)setCompletionBlock:(void (^)(void))block;
第一種方式:可以直接跟在任務(wù)后面編寫需要完成的操作,如這里在下載圖片后毅贮,緊跟著下載第二張圖片办悟。但是這種寫法有的時候把兩個不相關(guān)的操作寫到了一個代碼塊中,代碼的可閱讀性不強嫩码。
第二種方式:? 在上一個任務(wù)執(zhí)行完后誉尖,會執(zhí)行operation.completionBlock=^{}代碼段,且是在當前線程執(zhí)行铸题。
*/