iOS多線程GCD 、NSTread和NSOperation簡介

在iOS開發(fā)中我們經(jīng)常會(huì)用到多線程來處理一些業(yè)務(wù)褐荷,那么iOS里有哪些實(shí)現(xiàn)多線程的方式呢嘹悼?

  • NSTread:封裝程度最小、最輕量級(jí)其监,開銷較大限匣。
  • GCD(Grand Central Dispatch):內(nèi)部效率優(yōu)化,提供簡潔的C語言接口米死,更加簡單高效。
  • NSOperation:基于GCD的一個(gè)抽象基類峦筒,不需要管理線程的生命周期和同步,比GCD可控性強(qiáng)卤材。
一峦失、NSTread

NSTread封裝程度最小、最輕量級(jí)的多線程編程接口帆精,它使用更加靈活,但需要手動(dòng)管理線程的生命周期实幕、線程同步和線程加鎖等堤器,開銷較大闸溃。

/** NSThread 靜態(tài)工具方法 **/
    //1、是否開啟了多線程
    BOOL isMultiThread = [NSThread isMultiThreaded];
    //2辉川、獲取當(dāng)前線程
    NSThread *currentThread = [NSThread currentThread];
    //3、獲取主線程
    NSThread *mainThread = [NSThread mainThread];
    //4府蛇、睡眠當(dāng)前線程
    //4.1屿愚、線程睡眠5s
    [NSThread sleepForTimeInterval:5];
    //4.2、線程睡眠到指定時(shí)間妆距,效果同上
    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:5]];
    //5、退出當(dāng)前線程蚪黑,注意不要在主線程調(diào)用中剩,防止主線程被kill掉
    [NSThread exit];

NSThread的使用比較簡單,可以動(dòng)態(tài)創(chuàng)建并初始化NSThread對(duì)象结啼,對(duì)其進(jìn)行設(shè)置并啟動(dòng);也可以通過NSThread的靜態(tài)方法快速創(chuàng)建并啟動(dòng)新線程澡腾;

/** NSTread 線程對(duì)象的基本創(chuàng)建,target為入口方法所在的對(duì)象动分,selector為線程入口方法 **/
    //1红选、線程實(shí)例對(duì)象創(chuàng)建與設(shè)置
    NSThread *newThread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    //設(shè)置線程優(yōu)先級(jí)threadPriority(0~1.0),該屬性即將被拋棄坟乾,將使用qualityOfService代替
    //newThread.threadPriority = 1.0;
    newThread.qualityOfService = NSQualityOfServiceUserInteractive;
    //開啟線程
    [newThread start];
    //2蝶防、靜態(tài)方法快速創(chuàng)建并開啟新線程
    [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
    [NSThread detachNewThreadWithBlock:^{
        NSLog(@"block run");
    }];

此外NSObject提供了隱式快速創(chuàng)建NSThread線程的performSelector系列方法。

/** NSObject 基類隱式創(chuàng)建線程的一些靜態(tài)工具方法 **/
    //1殷费、在當(dāng)前線程上執(zhí)行方法,延遲2s
    [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
    //2详羡、在指定線程上執(zhí)行方法,不等待當(dāng)前線程
    [self performSelector:@selector(run) onThread:newThread withObject:nil waitUntilDone:NO];
    //3实柠、后臺(tái)異步執(zhí)行方法
    [self performSelectorInBackground:@selector(run) withObject:nil];
    //4、在主線程上執(zhí)行方法
    [self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];
二草则、GCD

GCD(Grand Central Dispatch)又叫大中央調(diào)度登钥,它對(duì)線程操作進(jìn)行了封裝,加入了很多新的特性牧牢,內(nèi)部進(jìn)行了效率優(yōu)化,提供了簡潔的C語言接口伯铣,使用更加簡單高效轮纫,也是蘋果公司推薦的方式。

GCD的用法特別靈活放前,在下一篇將詳細(xì)講解一下它的用法;這里主要掌握點(diǎn)分為以下幾個(gè)方面:

1凭语、串行隊(duì)列與并發(fā)隊(duì)列dispatch_queue_t撩扒;
/*
     創(chuàng)建隊(duì)列
     DISPATCH_QUEUE_SERIAL 表示串行隊(duì)列,隊(duì)列內(nèi)任務(wù)一個(gè)接一個(gè)的執(zhí)行炒辉,按照先進(jìn)先出(FIFO)的順序執(zhí)行
     DISPATCH_QUEUE_CONCURRENT 表示并發(fā)隊(duì)列,隊(duì)列內(nèi)任務(wù)可同時(shí)并列執(zhí)行黔寇,任務(wù)之間不會(huì)相互等待斩萌,執(zhí)行順序不可預(yù)測(cè)
     */
    // 串行隊(duì)列的創(chuàng)建方法
    dispatch_queue_t queueSerial = dispatch_queue_create("com.jzsec.GCDtest", DISPATCH_QUEUE_SERIAL);
    // 并發(fā)隊(duì)列的創(chuàng)建方法
    dispatch_queue_t queueCon = dispatch_queue_create("com.jzsec.GCDtest", DISPATCH_QUEUE_CONCURRENT);
2状囱、同步dispatch_sync與異步dispatch_async派發(fā)任務(wù);
dispatch_sync(queue, ^{
        // 追加任務(wù)1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時(shí)操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印當(dāng)前線程
        }
    });
dispatch_async(queue, ^{
        // 追加任務(wù)2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時(shí)操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印當(dāng)前線程
        }
    });
3袭艺、dispatch_once_t 只執(zhí)行一次;
/**
 * 一次性代碼(只執(zhí)行一次)dispatch_once
 能保證某段代碼在程序運(yùn)行過程中只被執(zhí)行1次猾编,并且即使在多線程的環(huán)境下,dispatch_once也可以保證線程安全轰传。
 */
- (void)once {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 只執(zhí)行1次的代碼(這里面默認(rèn)是線程安全的)
        NSLog(@"dispatch_once");
    });
}
4瘪撇、dispatch_after 延后執(zhí)行;
/**
 * 延時(shí)執(zhí)行方法 dispatch_after
 dispatch_after函數(shù)并不是在指定時(shí)間之后才開始執(zhí)行處理恕曲,而是在指定時(shí)間之后將任務(wù)追加到主隊(duì)列中渤涌。嚴(yán)格來說,這個(gè)時(shí)間并不是絕對(duì)準(zhǔn)確的实蓬,但想要大致延遲執(zhí)行任務(wù),dispatch_after函數(shù)是很有效的调鬓。
 */
- (void)after {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當(dāng)前線程
    NSLog(@"asyncMain---begin");
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 2.0秒后異步追加任務(wù)代碼到主隊(duì)列练俐,并開始執(zhí)行
        NSLog(@"after---%@",[NSThread currentThread]);  // 打印當(dāng)前線程
    });
}
5、dispatch_group_t 組調(diào)度燕锥;
/**
 * 隊(duì)列組 dispatch_group_notify
 當(dāng)group所有任務(wù)都執(zhí)行完成之后悯蝉,才執(zhí)行dispatch_group_notify block 中的任務(wù)。
 */
- (void)groupNotify {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印當(dāng)前線程
    NSLog(@"group---begin");
    
    dispatch_group_t group =  dispatch_group_create();
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任務(wù)1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時(shí)操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印當(dāng)前線程
        }
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任務(wù)2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時(shí)操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印當(dāng)前線程
        }
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 等前面的異步任務(wù)1暇榴、任務(wù)2都執(zhí)行完畢后,回到主線程執(zhí)行下邊任務(wù)
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模擬耗時(shí)操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印當(dāng)前線程
        }
        NSLog(@"group---end");
    });
}
三婆硬、NSOperation

基于GCD的一個(gè)抽象基類,將線程封裝成要執(zhí)行的操作彬犯,不需要管理線程的生命周期和同步查吊,比GCD可控性強(qiáng),例如加入操作依賴控制操作執(zhí)行順序宋列、設(shè)置操作隊(duì)列最大可并發(fā)執(zhí)行的才做個(gè)數(shù)和取消執(zhí)行等评也。

/** NSInvocationOperation 初始化 **/
    NSInvocationOperation *invoOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
    [invoOperation start];
    
    /** NSBlockOperation 初始化 **/
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"NSBlockOperationA:%@", [NSThread currentThread]);
    }];
    //blockOperation可以后續(xù)繼續(xù)添加block執(zhí)行塊,操作執(zhí)行后會(huì)在不同的線程并發(fā)執(zhí)行這些執(zhí)行塊嘹叫。
    [blockOperation addExecutionBlock:^{
        NSLog(@"NSBlockOperationB:%@", [NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
        NSLog(@"NSBlockOperationC:%@", [NSThread currentThread]);
    }];
    [blockOperation start];
    
    /** 獲取主隊(duì)列(主線程) **/
    NSOperationQueue *queue = [NSOperationQueue mainQueue];
    //創(chuàng)建a诈乒、b、c操作
    NSOperation *a = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"OperationA");
    }];
    NSOperation *b = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"OperationB");
    }];
    NSOperation *c = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"OperationC");
    }];
    //添加操作依賴喂饥,c依賴于a和b,這樣c一定會(huì)在a和b完成之后才執(zhí)行员帮,即順序?yàn)锳导饲、B、C
    [c addDependency:a];
    [c addDependency:b];
    //添加操作a硝岗、b、c 到操作隊(duì)列queue(特意將c在a和b之前添加)
    [queue addOperation:c];
    [queue addOperation:a];
    [queue addOperation:b];
四型檀、線程同步

對(duì)于UI的更新代碼,必須要寫在主線程上執(zhí)行才會(huì)及時(shí)有效听盖;當(dāng)當(dāng)前代碼不在主線程時(shí)裂七,需要將UI更新的部分代碼單獨(dú)同步到主線程背零。
同步的方法有三種:

  • NSThread類的performSelectorOnMainThread方法
  • NSOperation類的mainQueue主隊(duì)列
  • GCD的dispatch_get_main_queue()獲取主隊(duì)列

推薦直接使用GCD的方法:

dispatch_async(dispatch_get_main_queue( ), ^{
     //刷新UI的代碼
});

在iOS實(shí)際開發(fā)中无埃,還是使用GCD的情況比較多,這里只是簡單介紹對(duì)了三種多線程實(shí)現(xiàn)的方式录语,在下一篇將詳細(xì)介紹一下GCD的用法和注意事項(xiàng)澎埠。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末始藕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子江耀,更是在濱河造成了極大的恐慌诉植,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舌稀,死亡現(xiàn)場(chǎng)離奇詭異灼擂,居然都是意外死亡壁查,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門剔应,熙熙樓的掌柜王于貴愁眉苦臉地迎上來睡腿,“玉大人,你說我怎么就攤上這事峻贮∠郑” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵月洛,是天一觀的道長何恶。 經(jīng)常有香客問我,道長嚼黔,這世上最難降的妖魔是什么细层? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮盛撑,結(jié)果婚禮上抵卫,老公的妹妹穿的比我還像新娘胎撇。我一直安慰自己晚树,他們只是感情好爵憎,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布宝鼓。 她就那樣靜靜地躺著愚铡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沥寥。 梳的紋絲不亂的頭發(fā)上营曼,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天锻全,我揣著相機(jī)與錄音录煤,去河邊找鬼妈踊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛萝勤,可吹牛的內(nèi)容都是我干的呐伞。 我是一名探鬼主播伶氢,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼蜗巧,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼惧蛹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起迅腔,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤沧烈,失蹤者是張志新(化名)和其女友劉穎锌雀,沒想到半個(gè)月后腋逆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年上遥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了粉楚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖撵摆,靈堂內(nèi)的尸體忽然破棺而出特铝,到底是詐尸還是另有隱情鲫剿,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站政冻,受9級(jí)特大地震影響明场,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜逼泣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望秃励。 院中可真熱鬧夺鲜,春花似錦、人聲如沸床佳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽影兽。三九已至峻堰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間旦万,已是汗流浹背镶蹋。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國打工淆两, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秋冰,地道東北人踱葛。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像性含,于是被迫代替她去往敵國和親鸳惯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容