1. 什么是程序呜达、進程谣蠢、線程
1.1 程序:
由源代碼生成的可執(zhí)行應(yīng)用。(例如:QQ.APP)
1.2 進程:
一個正在運行的程序可以看做一個進程查近。(例如:正在運行的QQ就是一個進程)眉踱,進程擁有獨立運行所需的全部資源。
1.3 線程:
程序中獨立運行的代碼段霜威。(例如:接收QQ消息的代碼)
一個進程是由一或多個線程組成谈喳。進程只負責(zé)資源的調(diào)度和分配,線程才是程序真正的執(zhí)行單元戈泼,負責(zé)代碼的執(zhí)行婿禽。
2. 單線程與多線程有什么區(qū)別
2.1單線程
每個正在運行的程序(即進程),至少包含一個線程大猛,這個線程叫主線程扭倾。
主線程在程序啟動時被創(chuàng)建,用于執(zhí)行main函數(shù)胎署。
只有一個主線程的程序吆录,稱作單線程程序。
主線程負責(zé)執(zhí)行程序的所有代碼(UI展現(xiàn)以及刷新琼牧,網(wǎng)絡(luò)請求恢筝,本地存儲等等)。這些代碼只能順序執(zhí)行巨坊,無法并發(fā)執(zhí)行撬槽。
2.2多線程
擁有多個線程的程序,稱作多線程程序趾撵。
iOS允許用戶自己開辟新的線程侄柔,相對于主線程來講,這些線程占调,稱作子線程暂题。
可以根據(jù)需要開辟若干子線程
子線程和主線程是 都是 獨立的運行單元,各自的執(zhí)行互不影響究珊,因此能夠并發(fā)執(zhí)行薪者。
2.3區(qū)別
單線程程序:只有一個線程,代碼順序執(zhí)行剿涮,容易出現(xiàn)代碼阻塞(頁面假死)言津。
多線程程序:有多個線程攻人,線程間獨立運行,能有效的避免代碼阻塞悬槽,并且提高程序的運行性能怀吻。
注意:iOS中關(guān)于UI的添加和刷新必須在主線程中操作。
3. iOS多線程實現(xiàn)種類
主要由四種:NSThread初婆、NSoperationQueue蓬坡、NSobject、GCD
1.1輕量級別的多線程技術(shù)烟逊,
需要我們手動管理線程渣窜,還有提供的方法比較少,例如:串行宪躯,并發(fā)執(zhí)行這些實現(xiàn)起來相當(dāng)困難乔宿,開辟子線程的方法有兩種,這個需要我盟手動開啟線程访雪,也就是start方法详瑞,并且有返回值,返回值就是NSThread對象臣缀,可以設(shè)置線程名稱坝橡,設(shè)置線程的權(quán)限的等級一些操作參數(shù)。另一個是便利構(gòu)造器的方法開辟子線程精置,無返回值计寇,會自動啟動線程,不需要手動調(diào)用start方法脂倦。
#pragma mark -- NSThread 開辟子線程
// NSThread是我們自己手動開辟的子線程番宁,如果使用的是初始化方式就需要我們自己是釋放,如果使用的是便利構(gòu)造器方式它就會自動啟動赖阻,只要是我們手動開辟的線程蝶押,都需要我夢自己管理該線程,不只是啟動火欧,還有該線程使用完畢后的資源回收棋电,所以在NSThread的回調(diào)方法中需要加入自動釋放池來回收資源
- (void)threadInfo{
// object:這個是回調(diào)方法的參數(shù);
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(testThread:) object:@"我是參數(shù)"];
// 當(dāng)使用初始化方法出來的主線程需要start啟動
[thread start];
// NSLog(@"我是逗比");
// 可以為開辟的子線程起名字
thread.name = @"我是第二條線程";
// 調(diào)整Thread的權(quán)限 線程權(quán)限的范圍值為0 ~ 1 苇侵。越大權(quán)限越高赶盔,先執(zhí)行的概率就會越高,由于是概率榆浓,所以并不能很準(zhǔn)確的的實現(xiàn)我們想要的執(zhí)行順序于未,默認(rèn)值是0.5
thread.threadPriority = 1;
// 取消當(dāng)前已經(jīng)啟動的線程
// [thread cancel];
// 通過遍歷構(gòu)造器開辟子線程
// [NSThread detachNewThreadSelector:@selector(testThread:) toTarget:self withObject:@"便利構(gòu)造器方式"];
}
1.2回調(diào)方法
如果子線程是我們手動開辟的,那么就需要我們來管理它運行所造成的資源回收
- (void)testThread:(NSString *)testString{
// 如果子線程是我們手動開辟的,那么就需要我們來管理它運行所造成的資源回收
@autoreleasepool {
NSLog(@"參數(shù)----%@",testString);
NSLog(@"testThread -- %@",[NSThread currentThread]);
double sum = 0;
for (int i = 1; i < 635500000; i ++) {
sum += i;
}
NSLog(@"sum = %f---- %@",sum,[NSThread currentThread]);
// 回到主線程 所有的NSObject對象或者NSobject的子類都有該方法沉眶,他是NSthread的另一種體現(xiàn)方式
// waitUntilDone:是否將該回調(diào)方法執(zhí)行完在執(zhí)行后面的代碼,如果為YES:就必須等回調(diào)方法執(zhí)行完成之后才能執(zhí)行后面的代碼杉适,說白了就是阻塞當(dāng)前的線程谎倔,如果是NO:就是不等回調(diào)方法結(jié)束。不會阻塞當(dāng)前線程
// 回到主線程
[self performSelectorOnMainThread:@selector(backMainThread) withObject:nil waitUntilDone:YES];
}
}
- (void)backMainThread{
NSLog(@"回到主線程");
}
2.NSObject
只要是NSObject的子類或者對象都可以通過調(diào)用方法進入子線程和主線程猿推,其實這些方法所開辟的子線程也是NSThread的另一種體現(xiàn)方式片习。
開辟子線程:
[self performSelectorInBackground:@selector(aaa) withObject:nil];
進入主線程:
[self performSelectorOnMainThread:@selector(bbb) withObject:nil
waitUntilDone:YES];
3.3.NSOperationQueue:他是將一組事件添加到隊列中,如果想讓這組事件是在主線程中執(zhí)行蹬叭,那么就需要主隊列[NSOperationQueue mainQueue];如果想將一組事件在子線程中執(zhí)行那么就需要其他隊列[[NSOperationQueue alloc] init];NSOperationQueue就是事件藕咏,它本身是一個抽象類,如果需要實現(xiàn)具體操作秽五,需要他的兩個子類
NSInvocationOperation和NSBlockOperation;事件本身與線程無關(guān)孽查,知識看你將他加到那種隊列中。如果加到隊列中想要是的時間順序執(zhí)行坦喘,需要給事件添加依賴關(guān)系盲再,添加依賴關(guān)系的時候兩個事件不能互為依賴。也可以設(shè)置時間的優(yōu)先級來提高它先執(zhí)行的概率瓣铣,但是不準(zhǔn)確答朋,還可以設(shè)置隊列最大并發(fā)行數(shù),來是事件順序執(zhí)行棠笑。
- (void)invocationOperationInfo{
// NSInvocationOperation 這個類只執(zhí)行一個操作梦碗,本身與線程無關(guān),意思就是蓖救,你把該類對象放到哪個線程里面洪规,他就在那個線程中執(zhí)行
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector( invocationOperationAction) object:nil];
// 改事件需手動執(zhí)行
// [invocationOperation start];
// 通過Block方式增加一個事件
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block --- %@",[NSThread currentThread]);
}];
// block方式的operation可以增加一組額外的block事件,通過這種方式的blockOperation添加的block事件順序無法掌握沒所以線程無法掌控
for (int i = 0; i < 5; i ++) {
[blockOperation addExecutionBlock:^{
NSLog(@"execuBlock %@",[NSThread currentThread]);
}];
}
// 當(dāng)所有的block事件都執(zhí)行完了,我們就可以讓他發(fā)出通知藻糖,告訴我們所有的事件都執(zhí)行完了
blockOperation.completionBlock = ^{
NSLog(@"不管上面的小弟怎么鬧淹冰,我是最后一個");
NSLog(@"_____%@",[NSThread currentThread]);
};
// 主隊列 是將一組事件在主線程中執(zhí)行,不用設(shè)置任何屬性巨柒,一組事件都會順序執(zhí)行
// 我們需要按照順序來執(zhí)行一組事件樱拴,轉(zhuǎn)讓個時候該怎么辦? 有兩種方式
NSOperationQueue *queue = [NSOperationQueue mainQueue];
// 第一種:設(shè)置隊列最大并發(fā)執(zhí)行事件的?個數(shù)洋满,該屬性默認(rèn)值是-1晶乔;意思是:該隊列中有多少個事件,就并發(fā)執(zhí)行多少個牺勾,如果設(shè)置并發(fā)事件設(shè)置為1正罢,那就是一次只執(zhí)行一個事件,
// queue.maxConcurrentOperationCount = 1;
// 第二種:通過添加事件依賴驻民,事件依賴的意思就是說:當(dāng)?shù)匾粋€事件執(zhí)行完畢之后才執(zhí)行另一個事件翻具,在這里就是先執(zhí)行invocationOperation履怯,在執(zhí)行blockOperation,
[blockOperation addDependency:invocationOperation];
// 給隊列中增加事件
[queue addOperation:invocationOperation];
[queue addOperation:blockOperation];
}
4.GCD:
GCD效率比operationQueue要高一些裆泳,功能更強一些叹洲,目前有替代其他多線程的趨勢,他處理時間主要通過隊列來執(zhí)行工禾,分為兩種隊列运提,一種是串行,另一中是并行闻葵,系統(tǒng)提供給我們的是全局隊列民泵,一種是主隊列,添加時間函數(shù)為dispatch_async()槽畔;一般我們都用一部添加事件栈妆,最重要的原因是他不會阻塞當(dāng)前線程,全局隊列中所添加的異步事件坑定都是子線程中的厢钧,主隊列中添加事件不管同步還是異步都在主函數(shù)中運行签钩;!;悼臁铅檩!
一定記住會主線程要刷新UI原因是:1.iOS中為了效率更高,多數(shù)線程是沒有安全保證的莽鸿,在子線程中刷新UI有可能會遇到不可預(yù)知的錯誤昧旨,2.在子線程中刷新UI,只有當(dāng)前線程執(zhí)行完成祥得,才會刷新UI兔沃,和可能造成UI刷新不及時,影響用戶體驗级及,而且一般只有主線程才有UI刷新功能
創(chuàng)建一個串行隊列
- (void)serialQueuueCuanXing{
// 創(chuàng)建一個串行隊列
// dispatch_queue_create 函數(shù)是用來創(chuàng)建隊列使用乒疏,第一個參數(shù)為該隊列的標(biāo)簽,第二個參數(shù)為該隊列類型
dispatch_queue_t serialQueue = dispatch_queue_create("串行", DISPATCH_QUEUE_SERIAL);
// 給該隊列添加事件
// 第一個參數(shù)為該事件所在的隊列饮焦,第二個參數(shù)為Block怕吴,該事件索要做的處理
dispatch_sync(serialQueue, ^{
NSLog(@"叼毛獸出生了---%@",[NSThread currentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"叼毛獸會穿衣服了---%@",[NSThread currentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"叼毛獸掛墻上了----%@",[NSThread currentThread]);
});
NSLog(@"執(zhí)行完了-- %@",[NSThread currentThread]);
}
創(chuàng)建一個并行隊列
// 并行隊列
- (void)seriaQueueBingXing{
dispatch_queue_t seriaQueue = dispatch_queue_create("并行", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(seriaQueue, ^{
NSLog(@"我是第一---%@",[NSThread currentThread]);
});
dispatch_async(seriaQueue, ^{
NSLog(@"我是第二---%@",[NSThread currentThread]);
});
// 此函數(shù)會阻塞當(dāng)前線程,對主線程無影響。
dispatch_barrier_async(seriaQueue, ^{
NSLog(@"我正在執(zhí)行---%@",[NSThread currentThread]);
});
dispatch_async(seriaQueue, ^{
NSLog(@"我是第三---%@",[NSThread currentThread]);
});
dispatch_async(seriaQueue, ^{
NSLog(@"我是第四---%@",[NSThread currentThread]);
});
}
5.系統(tǒng)提供的全局隊列
- (void)globalQueue{
// 上面都是我們自主創(chuàng)建的隊列县踢,一般使用中转绷,我們都不會自己創(chuàng)建,而是使用系統(tǒng)提供的隊列硼啤,系統(tǒng)提供的隊列有全局隊列议经,在此隊列中可以添加多個異步事件,并發(fā)執(zhí)行
// 一般我們都是使用系統(tǒng)提供的們不用自己創(chuàng)建
// 第一個參數(shù)為該全局隊列的優(yōu)先級
// 第二個參數(shù)暫時沒用,是系統(tǒng)為后面擴展來使用的煞肾,在將來的某一天使用到咧织,直接賦0就可以了
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
NSLog(@"正在網(wǎng)絡(luò)下載或者其他一些耗時操作");
// 耗時操作完成之后回主線程更新UI,GCD回主線程方式
// 要得到主隊列,和operationQueue中的mainQueue是一樣的概念
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
// 在此處進行UI刷新
NSLog(@"mainQueue -- %@",[NSThread currentThread]);
});
});
}