1. Objective-C 中創(chuàng)建線程的方法是什么?如果在主線程中執(zhí)行代碼驱负,方法是什么作箍?如果想延時執(zhí)行代碼、方法又是什么合武?
線程創(chuàng)建有三種方法:使用NSThread創(chuàng)建临梗、使用GCD的dispatch、使用子類化的NSOperation,然后將其加入NSOperationQueue;在主線程執(zhí)行代碼稼跳。
方法是performSelectorOnMainThread盟庞,如果想延時執(zhí)行代碼可以用performSelector:onThread:withObject:waitUntilDone:。
2. 有哪些對象和變量需要釋放掉汤善?
如:線程的queue什猖, 繪制圖片的上下文等;
3. RunLoop 的5種運行方式是什么红淡?
kCFRunLoopDefaultMode:
UITrackingRunLoopMode:
UIInitializationaRunLoopMode:
GSEventReceiveRunLoopMode:
KCFCommonRunLoopModes:
4.我們說的OC是動態(tài)運行時語言是什么意思不狮?
主要是將數(shù)據(jù)類型的確定由編譯時,推遲到了運行時在旱。
簡單來說, 運行時機制使我們直到運行時才去決定一個對象的類別,以及調(diào)用該類別對象指定方法摇零。
5. GCD 與 NSOperation 的區(qū)別:
GCD 和 NSOperation 都是用于實現(xiàn)多線程:
GCD 基于C語言的底層API,GCD主要與block結(jié)合使用桶蝎,代碼簡潔高效驻仅。
NSOperation 屬于Objective-C類,是基于GCD更高一層的封裝登渣。復(fù)雜任務(wù)一般用NSOperation實現(xiàn)噪服。
6. 寫出使用GCD方式從子線程回到主線程的方法代碼
dispatch_sync(dispatch_get_main_queue(), ^{ });
7. 如何用GCD同步若干個異步調(diào)用?(如根據(jù)若干個url異步加載多張圖片胜茧,然后在都下載完成后合成一張整圖)
// 使用Dispatch Group追加block到Global Group Queue,這些block如果全部執(zhí)行完畢粘优,就會執(zhí)行Main Dispatch Queue中的結(jié)束處理的block。
// 創(chuàng)建隊列組
dispatch_group_t group = dispatch_group_create();
// 獲取全局并發(fā)隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{ /*加載圖片1 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片2 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片3 */ });
// 當(dāng)并發(fā)隊列組中的任務(wù)執(zhí)行完畢后才會執(zhí)行這里的代碼
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 合并圖片
});
8. dispatch_barrier_async(柵欄函數(shù))的作用是什么竹揍?
函數(shù)定義:dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
作用:
1.在它前面的任務(wù)執(zhí)行結(jié)束后它才執(zhí)行敬飒,它后面的任務(wù)要等它執(zhí)行完成后才會開始執(zhí)行。
2.避免數(shù)據(jù)競爭
// 1.創(chuàng)建并發(fā)隊列
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
// 2.向隊列中添加任務(wù)
dispatch_async(queue, ^{ // 1.2是并行的
NSLog(@"任務(wù)1, %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)2, %@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"任務(wù) barrier, %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{ // 這兩個是同時執(zhí)行的
NSLog(@"任務(wù)3, %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)4, %@",[NSThread currentThread]);
});
// 輸出結(jié)果: 任務(wù)1 任務(wù)2 ——》 任務(wù) barrier ——》任務(wù)3 任務(wù)4
// 其中的任務(wù)1與任務(wù)2芬位,任務(wù)3與任務(wù)4 由于是并行處理先后順序不定无拗。
參考資料:GCD(III)
9. 以下代碼運行結(jié)果如何?
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3");
}
// 只輸出:1昧碉。(主線程死鎖)
10. 什么是 RunLoop?
從字面上講就是運行循環(huán)英染,它內(nèi)部就是do-while循環(huán)揽惹,在這個循環(huán)內(nèi)部不斷地處理各種任務(wù)。
一個線程對應(yīng)一個RunLoop四康,基本作用就是保持程序的持續(xù)運行搪搏,處理app中的各種事件。通過runloop闪金,有事運行疯溺,沒事就休息,可以節(jié)省cpu資源哎垦,提高程序性能囱嫩。
主線程的run loop默認是啟動的。iOS的應(yīng)用程序里面漏设,程序啟動后會有一個如下的main()函數(shù)
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
參考資料:RunLoop 詳細介紹
11. OC中創(chuàng)建線程的方法是什么墨闲?如果在主線程中執(zhí)行代碼,方法是什么郑口?
// 創(chuàng)建線程的方法
- [NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil]
- [self performSelectorInBackground:nil withObject:nil];
- [[NSThread alloc] initWithTarget:nil selector:nil object:nil];
- dispatch_async(dispatch_get_global_queue(0, 0), ^{});
- [[NSOperationQueue new] addOperation:nil];
// 主線程中執(zhí)行代碼的方法
- [self performSelectorOnMainThread:(SEL)aSelector withObject:nil waitUntilDone:YES];
- dispatch_async(dispatch_get_main_queue(), ^{});
- [[NSOperationQueue mainQueue] addOperation:nil];
12. 你用過NSOperationQueue么鸳碧?如果用過或者了解的話,你為什么要使用NSOperationQueue犬性,實現(xiàn)了什么瞻离?請描述它和G.C.D的區(qū)別和類似的地方(提示:可以從兩者的實現(xiàn)機制和適用范圍來描述)
使用NSOperationQueue用來管理子類化的NSOperation對象,控制
其線程并發(fā)數(shù)目仔夺。GCD和NSOperation都可以實現(xiàn)對線程的管理琐脏,區(qū)別
是 NSOperation和NSOperationQueue是多線程的面向?qū)ο蟪橄蟆m椖恐?使用NSOperation的優(yōu)點是NSOperation是對線程的高度抽象缸兔,在項目中
使用它日裙,會使項目的程序結(jié)構(gòu)更好,子類化NSOperation的設(shè)計思路惰蜜,
是具有面向?qū)ο蟮膬?yōu)點(復(fù)用昂拂、封裝),使得實現(xiàn)是多線程支持抛猖,而接
口簡單格侯,建議在復(fù)雜項目中使用。
項目中使用GCD的優(yōu)點是GCD本身非常簡單财著、易用联四,對于不復(fù)雜的多線
程操作,會節(jié)省代碼量撑教,而Block參數(shù)的使用朝墩,會是代碼更為易讀,建議
在簡單項目中使用伟姐。
13. iOS 的幾種線程鎖收苏?
互斥鎖
:用于多線程編程亿卤,防止兩條線程同時對同一公共資源進行讀寫的機制;
@synchronized 簡單鹿霸、效率最低排吴。
NSLock 不能多次調(diào)用 lock方法,會造成死鎖;
pthread_mutex
自旋鎖
:采用信號的一種同步方式
dispatch_semaphore 使用信號量來獲取更多的取值空間懦鼠,用來實現(xiàn)更加復(fù)雜的同步钻哩,而不單單是線程間互斥;
OSSpinLock
遞歸鎖
:同一個線程可以加鎖N次而不會引發(fā)死鎖
NSRecursiveLock
條件鎖
: 條件變量葛闷,當(dāng)進程的某些資源要求不滿足時就進入休眠憋槐,也就是鎖住了。當(dāng)資源被分配到了淑趾,條件鎖打開,進程繼續(xù)運行
NSCondition
NSConditionLock
遵循NSLocking協(xié)議忧陪,使用的時候同樣是lock,unlock加解鎖扣泊,wait是傻等,waitUntilDate:方法是等一會嘶摊,都會阻塞掉線程延蟹,signal是喚起一個在等待的線程,broadcast是廣播全部喚起叶堆。
讀寫鎖
//加讀鎖
pthread_rwlock_rdlock(&rwlock);
//解鎖
pthread_rwlock_unlock(&rwlock);
//加寫鎖
pthread_rwlock_wrlock(&rwlock);
//解鎖
pthread_rwlock_unlock(&rwlock);
14. NSThread阱飘、NSOperationQueue、GCD 3種線程的認知
1) NSThread 是這三種范式里面相對輕量級的虱颗,但也是使用起來最負責(zé)的沥匈,
你需要自己管理thread的生命周期,線程之間的同步忘渔。線程共享同一應(yīng)用程序的部分內(nèi)存空間高帖,
它們擁有對數(shù)據(jù)相同的訪問權(quán)限。你得協(xié)調(diào)多個線程對同一數(shù)據(jù)的訪問畦粮,
一般做法是在訪問之前加鎖散址,這會導(dǎo)致一定的性能開銷。
2) NSOperationQueue 以面向?qū)ο蟮姆绞椒庋b了用戶需要執(zhí)行的操作宣赔,
我們只要聚焦于我們需要做的事情预麸,而不必太操心線程的管理,同步等事情儒将,
因為NSOperation已經(jīng)為我們封裝了這些事情吏祸。
NSOperation 是一個抽象基類,我們必須使用它的子類椅棺。
3) GCD: iOS4 才開始支持犁罩,它提供了一些新的特性齐蔽,以及運行庫來支持多核并行編程,
它的關(guān)注點更高:如何在多個cpu上提升效率床估。
總結(jié):
- NSThread是早期的多線程解決方案含滴,實際上是把C語言的PThread線程管理代碼封裝成OC代碼。
- GCD是取代NSThread的多線程技術(shù)丐巫,C語法+block谈况。功能強大。
- NSOperationQueue是把GCD封裝為OC語法递胧,額外比GCD增加了幾項新功能碑韵。
* 最大線程并發(fā)數(shù)
* 取消隊列中的任務(wù)
* 暫停隊列中的任務(wù)
* 可以調(diào)整隊列中的任務(wù)執(zhí)行順序,通過優(yōu)先級
* 線程依賴
* NSOperationQueue支持KVO缎脾。這就意味著你可以觀察任務(wù)的狀態(tài)屬性祝闻。
但是NSOperationQueue的執(zhí)行效率沒有GCD高,所以一半情況下遗菠,我們使用GCD來完成多線程操作联喘。
15. 多個網(wǎng)絡(luò)請求完成后執(zhí)行下一步,有幾種解決方案辙纬?
使用dispatch_group
-(void)Btn2{
NSString *str = @"http://www.reibang.com/p/6930f335adba";
NSURL *url = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
dispatch_group_t downloadGroup = dispatch_group_create();
for (int i=0; i<10; i++) {
dispatch_group_enter(downloadGroup);
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"%d---%d",i,i);
dispatch_group_leave(downloadGroup);
}];
[task resume];
}
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
NSLog(@"end");
});
}
? 創(chuàng)建一個dispatch_group_t豁遭, 每次網(wǎng)絡(luò)請求前先dispatch_group_enter,請求回調(diào)后再dispatch_group_leave,對于enter和leave必須配合使用,有幾次enter就要有幾次leave贺拣,否則group會一直存在蓖谢。當(dāng)所有enter的block都leave后,會執(zhí)行dispatch_group_notify的block譬涡。
采用信號量dispatch_semaphore_t
-(void)Btn3{
NSString *str = @"http://www.reibang.com/p/6930f335adba";
NSURL *url = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
for (int i=0; i<10; i++) {
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"%d---%d",i,i);
count++;
if (count==10) {
dispatch_semaphore_signal(sem);
count = 0;
}
}];
[task resume];
}
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"end");
});
}
dispatch_semaphore信號量為基于計數(shù)器的一種多線程同步機制闪幽。如果semaphore計數(shù)大于等于1,計數(shù)-1昂儒,返回沟使,程序繼續(xù)運行。如果計數(shù)為0渊跋,則等待腊嗡。dispatch_semaphore_signal(semaphore)為計數(shù)+1操作,dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)為設(shè)置等待時間,這里設(shè)置的等待時間是一直等待拾酝。
對于以上代碼通俗一點就是燕少,開始為0,等待蒿囤,等10個網(wǎng)絡(luò)請求都完成了客们,dispatch_semaphore_signal(semaphore)為計數(shù)+1,然后計數(shù)-1返回,程序繼續(xù)執(zhí)行底挫。(這里也就是為什么有個count變量的原因恒傻,記錄網(wǎng)絡(luò)回調(diào)的次數(shù),回調(diào)10次之后再發(fā)信號量建邓,使后面程序繼續(xù)運行)盈厘。
使用dispatch_barrier_async(柵欄函數(shù))
dispatch_barrier_sync(dispatch_queue_t queue, ^{
})
在它前面的任務(wù)執(zhí)行結(jié)束后它才執(zhí)行,它后面的任務(wù)要等它執(zhí)行完成后才會開始執(zhí)行官边,
避免數(shù)據(jù)競爭
GCD(III)
15. OC 的鎖有哪些沸手?
@synchronized. 加鎖的對象需要是同一個對象
NSLock 對象鎖。 多次lock死鎖
NSRecursiveLock 遞歸鎖注簿。 場景限制
NSConditionLock 條件鎖契吉。
pthread_mutex (C語言)互斥鎖 linux 底層
dispatch_semaphore (GCD)。 信號量
OSSpinLock (不建議使用)
16. 自旋和互斥對比诡渴?
相同點:都能保證同一時間只有一個線程訪問共享資源捐晶。都能保證線程安全。
不同點:
互斥鎖:如果共享數(shù)據(jù)已經(jīng)有其他線程加鎖了妄辩,線程會進入休眠狀態(tài)等待鎖租悄。一旦被訪問的資源被解鎖,則等待資源的線程會被喚醒恩袱。
自旋鎖:如果共享數(shù)據(jù)已經(jīng)有其他線程加鎖了,線程會以死循環(huán)的方式等待鎖胶哲,一旦被訪問的資源被解鎖畔塔,則等待資源的線程會立即執(zhí)行。
自旋鎖的效率高于互斥鎖鸯屿。
17. 使用以上鎖需要注意哪些澈吨?
使用自旋鎖時要注意:
由于自旋時不釋放CPU,因而持有自旋鎖的線程應(yīng)該盡快釋放自旋鎖寄摆,否則等待該自旋鎖的線程會一直在哪里自旋谅辣,這就會浪費CPU時間。
持有自旋鎖的線程在sleep之前應(yīng)該釋放自旋鎖以便其它線程可以獲得該自旋鎖婶恼。內(nèi)核編程中桑阶,如果持有自旋鎖的代碼sleep了就可能導(dǎo)致整個系統(tǒng)掛起。
使用任何鎖都需要消耗系統(tǒng)資源(內(nèi)存資源和CPU時間)勾邦,這種資源消耗可以分為兩類:
1.建立鎖所需要的資源
2.當(dāng)線程被阻塞時所需要的資源
使用互斥鎖的注意:
由于是互斥鎖蚣录,當(dāng)一個線程進行訪問的時候,該線程獲得鎖眷篇,其他線程進行訪問的時候萎河,將被操作系統(tǒng)掛起,直到該線程釋放鎖,其他線程才能對其進行訪問虐杯,從而卻確保了線程安全玛歌。但是如果連續(xù)鎖定兩次,則會造成死鎖問題擎椰。
兩種鎖的加鎖原理:
互斥鎖:線程會從sleep(加鎖)——>running(解鎖)支子,過程中有上下文的切換(主動出讓時間片,線程休眠确憨,等待下一次喚醒)译荞,cpu的搶占,信號的發(fā)送等開銷休弃。
自旋鎖:線程一直是running(加鎖——>解鎖)吞歼,死循環(huán)(忙等 do-while)檢測鎖的標志位,機制不復(fù)雜塔猾。
``
``
``
``
``
``