實(shí)例化講解RunLoop

轉(zhuǎn)自此處

實(shí)例化講解RunLoop

之前看過很多有關(guān)RunLoop的文章诗祸,其中要么是主要介紹RunLoop的基本概念,要么是主要講解RunLoop的底層原理纫版,很少用真正的實(shí)例來講解RunLoop的传透,這其中有大部分原因是由于大家在項(xiàng)目中很少能用到RunLoop吧凉唐。基于這種原因湿痢,本文中將用很少的篇幅來對基礎(chǔ)內(nèi)容做以介紹涝缝,然后主要利用實(shí)例來加深大家對RunLoop的理解。本文主要分為如下幾個(gè)部分:

  • RunLoop的基礎(chǔ)知識(shí)

  • 初識(shí)RunLoop譬重,如何讓RunLoop進(jìn)駐線程

  • 深入理解Perform Selector

  • 一直"活著"的后臺(tái)線程

  • 深入理解NSTimer

  • 讓兩個(gè)后臺(tái)線程有依賴性的一種方式

  • NSURLConnetction的內(nèi)部實(shí)現(xiàn)

  • AFNetWorking中是如何使用RunLoop的?

  • 其它:利用GCD實(shí)現(xiàn)定時(shí)器功能

  • 延伸閱讀

一拒逮、RunLoop的基本概念:

什么是RunLoop?提到RunLoop臀规,我們一般都會(huì)提到線程滩援,這是為什么呢?先來看下官方對RunLoop的定義:RunLoop系統(tǒng)中和線程相關(guān)的基礎(chǔ)架構(gòu)的組成部分(和線程相關(guān))塔嬉,一個(gè)RunLoop是一個(gè)事件處理環(huán)玩徊,系統(tǒng)利用這個(gè)事件處理環(huán)來安排事務(wù)租悄,協(xié)調(diào)輸入的各種事件。RunLoop的目的是讓你的線程在有工作的時(shí)候忙碌恩袱,沒有工作的時(shí)候休眠(和線程相關(guān))泣棋。可能這樣說你還不是特別清楚RunLoop究竟是用來做什么的畔塔,打個(gè)比方來說明:我們把線程比作一輛跑車潭辈,把這輛跑車的主人比作RunLoop,那么在沒有'主人'的時(shí)候澈吨,這個(gè)跑車的生命是直線型的把敢,其啟動(dòng),運(yùn)行完之后就會(huì)廢棄(沒有人對其進(jìn)行控制谅辣,'撞壞'被收回)技竟,當(dāng)有了RunLoop這個(gè)主人之后,‘線程’這輛跑車的生命就有了保障屈藐,這個(gè)時(shí)候榔组,跑車的生命是環(huán)形的,并且在主人有比賽任務(wù)的時(shí)候就會(huì)被RunLoop這個(gè)主人所喚醒,在沒有任務(wù)的時(shí)候可以休眠(在IOS中联逻,開啟線程是很消耗性能的搓扯,開啟主線程要消耗1M內(nèi)存,開啟一個(gè)后臺(tái)線程需要消耗512k內(nèi)存包归,我們應(yīng)當(dāng)在線程沒有任務(wù)的時(shí)候休眠锨推,來釋放所占用的資源,以便CPU進(jìn)行更加高效的工作)公壤,這樣可以增加跑車的效率,也就是說RunLoop是為線程所服務(wù)的换可。這個(gè)例子有點(diǎn)不是很貼切,線程和RunLoop之間是以鍵值對的形式一一對應(yīng)的厦幅,其中key是thread沾鳄,value是runLoop(這點(diǎn)可以從蘋果公開的源碼中看出來),其實(shí)RunLoop是管理線程的一種機(jī)制确憨,這種機(jī)制不僅在IOS上有译荞,在Node.js中的EventLoop,Android中的Looper休弃,都有類似的模式吞歼。剛才所說的比賽任務(wù)就是喚醒跑車這個(gè)線程的一個(gè)source;RunLoop Mode就是,一系列輸入的source,timer以及observer塔猾,RunLoop Mode包含以下幾種:

NSDefaultRunLoopMode,
NSEventTrackingRunLoopMode,
UIInitializationRunLoopMode,
NSRunLoopCommonModes,
NSConnectionReplyMode,
NSModalPanelRunLoopMode

至于這些mode各自的含義篙骡,讀者可自己查詢,網(wǎng)上不乏這類資源;

二、初識(shí)RunLoop糯俗,如何讓RunLoop進(jìn)駐線程

我們在主線程中添加如下代碼:

while (1) {
    NSLog(@"while begin");
    // the thread be blocked here
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    // this will not be executed
    NSLog(@"while end");
}

這個(gè)時(shí)候我們可以看到主線程在執(zhí)行完[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; 之后被阻塞而沒有執(zhí)行下面的NSLog(@"while end");同時(shí)慎皱,我們利用GCD,將這段代碼放到一個(gè)后臺(tái)線程中:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    while (1) {
        NSLog(@"while begin");
        NSRunLoop *subRunLoop = [NSRunLoop currentRunLoop];
        [subRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        NSLog(@"while end");
    }
});

這個(gè)時(shí)候我們發(fā)現(xiàn)這個(gè)while循環(huán)會(huì)一直在執(zhí)行叶骨;這是為什么呢?我們先將這兩個(gè)RunLoop分別打印出來:

主線程的RunLoop

由于這個(gè)日志比較長茫多,我就只截取了上面的一部分。我們再看我們新建的子線程中的RunLoop,打印出來之后:

backGroundThreadRunLoop.png

從中可以看出來:我們新建的線程中:

sources0 = (null),
sources1 = (null),
observers = (null),
timers = (null)

我們看到雖然有Mode忽刽,但是我們沒有給它soures,observer,timer天揖,其實(shí)Mode中的這些source,observer,timer,統(tǒng)稱為這個(gè)Mode的item跪帝,如果一個(gè)Mode中一個(gè)item都沒有今膊,則這個(gè)RunLoop會(huì)直接退出,不進(jìn)入循環(huán)(其實(shí)線程之所以可以一直存在就是由于RunLoop將其帶入了這個(gè)循環(huán)中)伞剑。下面我們?yōu)檫@個(gè)RunLoop添加個(gè)source:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        while (1) {
        NSPort *macPort = [NSPort port];
        NSLog(@"while begin");
        NSRunLoop *subRunLoop = [NSRunLoop currentRunLoop];
        [subRunLoop addPort:macPort forMode:NSDefaultRunLoopMode];
        [subRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        NSLog(@"while end");
        NSLog(@"%@",subRunLoop);
    }    
});

這樣我們可以看到能夠?qū)崿F(xiàn)了和主線程中相同的效果斑唬,線程在這個(gè)地方暫停了,為什么呢黎泣?我們明天讓RunLoop在distantFuture之前都一直run的八×酢?相信大家已經(jīng)猜出出來了抒倚。這個(gè)時(shí)候線程被RunLoop帶到‘坑’里去了褐着,這個(gè)‘坑’就是一個(gè)循環(huán),在循環(huán)中這個(gè)線程可以在沒有任務(wù)的時(shí)候休眠托呕,在有任務(wù)的時(shí)候被喚醒含蓉;當(dāng)然我們只用一個(gè)while(1)也可以讓這個(gè)線程一直存在,但是這個(gè)線程會(huì)一直在喚醒狀態(tài)项郊,及時(shí)它沒有任務(wù)也一直處于運(yùn)轉(zhuǎn)狀態(tài)馅扣,這對于CPU來說是非常不高效的。

小結(jié):我們的RunLoop要想工作着降,必須要讓它存在一個(gè)Item(source,observer或者timer)差油,主線程之所以能夠一直存在,并且隨時(shí)準(zhǔn)備被喚醒就是應(yīng)為系統(tǒng)為其添加了很多Item

三鹊碍、深入理解Perform Selector

我們先在主線程中使用下performselector:

- (void)tryPerformSelectorOnMianThread{

[self performSelector:@selector(mainThreadMethod) withObject:nil]; }

- (void)mainThreadMethod{

NSLog(@"execute %s",__func__);

// print: execute -[ViewController mainThreadMethod]
}

這樣我們在ViewDidLoad中調(diào)用tryPerformSelectorOnMianThread,就會(huì)立即執(zhí)行厌殉,并且輸出:print: execute -[ViewController mainThreadMethod];

和上面的例子一樣,我們使用GCD,讓這個(gè)方法在后臺(tái)線程中執(zhí)行

 - (void)tryPerformSelectorOnBackGroundThread{

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

[self performSelector:@selector(backGroundThread) onThread:[NSThread currentThread] withObject:nil waitUntilDone:NO];

});
}
- (void)backGroundThread{

NSLog(@"%u",[NSThread isMainThread]);

NSLog(@"execute %s",__FUNCTION__);

}

同樣的侈咕,我們調(diào)用tryPerformSelectorOnBackGroundThread這個(gè)方法,我們會(huì)發(fā)現(xiàn)器紧,下面的backGroundThread不會(huì)被調(diào)用耀销,這是什么原因呢?

這是因?yàn)椋谡{(diào)用performSelector:onThread: withObject: waitUntilDone的時(shí)候熊尉,系統(tǒng)會(huì)給我們創(chuàng)建一個(gè)Timer的source罐柳,加到對應(yīng)的RunLoop上去,然而這個(gè)時(shí)候我們沒有RunLoop,如果我們加上RunLoop:

 - (void)tryPerformSelectorOnBackGroundThread{

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

[self performSelector:@selector(backGroundThread) onThread:[NSThread currentThread] withObject:nil waitUntilDone:NO];

NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop run];

});
}

這時(shí)就會(huì)發(fā)現(xiàn)我們的方法正常被調(diào)用了狰住。那么為什么主線程中的perfom selector卻能夠正常調(diào)用呢张吉?通過上面的例子相信你已經(jīng)猜到了,主線程的RunLoop是一直存在的催植,所以我們在主線程中執(zhí)行的時(shí)候肮蛹,無需再添加RunLoop。

小結(jié):當(dāng)perform selector在后臺(tái)線程中執(zhí)行的時(shí)候创南,這個(gè)線程必須有一個(gè)開啟的runLoop

四伦忠、一直"活著"的后臺(tái)線程

現(xiàn)在有這樣一個(gè)需求,每點(diǎn)擊一下屏幕稿辙,讓子線程做一個(gè)任務(wù),然后大家一般會(huì)想到這樣的方式:

@interface ViewController ()

@property(nonatomic,strong) NSThread *myThread;

@end

@implementation ViewController

 - (void)alwaysLiveBackGoundThread{

NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(myThreadRun) object:@"etund"];
self.myThread = thread;
[self.myThread start];

}
- (void)myThreadRun{

NSLog(@"my thread run");

}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    NSLog(@"%@",self.myThread);
    [self performSelector:@selector(doBackGroundThreadWork) onThread:self.myThread withObject:nil waitUntilDone:NO];
}
- (void)doBackGroundThreadWork{

    NSLog(@"do some work %s",__FUNCTION__);

}
@end

這個(gè)方法中昆码,我們利用一個(gè)強(qiáng)引用來獲取了后臺(tái)線程中的thread,然后在點(diǎn)擊屏幕的時(shí)候,在這個(gè)線程上執(zhí)行doBackGroundThreadWork這個(gè)方法邻储,此時(shí)我們可以看到赋咽,在touchesBegin方法中,self.myThread是存在的吨娜,但是這是為是什么呢冬耿?這就要從線程的五大狀態(tài)來說明了:新建狀態(tài)、就緒狀態(tài)萌壳、運(yùn)行狀態(tài)亦镶、阻塞狀態(tài)、死亡狀態(tài)袱瓮,這個(gè)時(shí)候盡管內(nèi)存中還有線程缤骨,但是這個(gè)線程在執(zhí)行完任務(wù)之后已經(jīng)死亡了,經(jīng)過上面的論述尺借,我們應(yīng)該怎樣處理呢绊起?我們可以給這個(gè)線程的RunLoop添加一個(gè)source,那么這個(gè)線程就會(huì)檢測這個(gè)source等待執(zhí)行燎斩,而不至于死亡(有工作的強(qiáng)烈愿望而不死亡):

 - (void)myThreadRun{

 [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode]; 
 [[NSRunLoop currentRunLoop] run]

  NSLog(@"my thread run");

}

這個(gè)時(shí)候再次點(diǎn)擊屏幕虱歪,我們就會(huì)發(fā)現(xiàn),后臺(tái)線程中執(zhí)行的任務(wù)可以正常進(jìn)行了栅表。

小結(jié):正常情況下笋鄙,后臺(tái)線程執(zhí)行完任務(wù)之后就處于死亡狀態(tài),我們要避免這種情況的發(fā)生可以利用RunLoop怪瓶,并且給它一個(gè)Source這樣來保證線程依舊還在

五萧落、深入理解NSTimer

我們平時(shí)使用NSTimer,一般是在主線程中的,代碼大多如下:

 - (void)tryTimerOnMainThread{

NSTimer *myTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self       
    selector:@selector(timerAction) userInfo:nil repeats:YES];

[myTimer fire];

}

- (void)timerAction{

NSLog(@"timer action");

}

這個(gè)時(shí)候代碼按照我們預(yù)定的結(jié)果運(yùn)行找岖,如果我們把這個(gè)Tiemr放到后臺(tái)線程中呢?

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    NSTimer *myTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];

    [myTimer fire];

});

這個(gè)時(shí)候我們會(huì)發(fā)現(xiàn)陨倡,這個(gè)timer只執(zhí)行了一次,就停止了许布。這是為什么呢兴革?通過上面的講解,想必你已經(jīng)知道了蜜唾,NSTimer,只有注冊到RunLoop之后才會(huì)生效杂曲,這個(gè)注冊是由系統(tǒng)自動(dòng)給我們完成的,既然需要注冊到RunLoop,那么我們就需要有一個(gè)RunLoop,我們在后臺(tái)線程中加入如下的代碼:

NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop run];

這樣我們就會(huì)發(fā)現(xiàn)程序正常運(yùn)行了。在Timer注冊到RunLoop之后灵妨,RunLoop會(huì)為其重復(fù)的時(shí)間點(diǎn)注冊好事件解阅,比如1:10,1:20泌霍,1:30這幾個(gè)時(shí)間點(diǎn)货抄。有時(shí)候我們會(huì)在這個(gè)線程中執(zhí)行一個(gè)耗時(shí)操作,這個(gè)時(shí)候RunLoop為了節(jié)省資源朱转,并不會(huì)在非常準(zhǔn)確的時(shí)間點(diǎn)回調(diào)這個(gè)Timer蟹地,這就造成了誤差(Timer有個(gè)冗余度屬性叫做tolerance,它標(biāo)明了當(dāng)前點(diǎn)到后,容許有多少最大誤差)藤为,可以在執(zhí)行一段循環(huán)之后調(diào)用一個(gè)耗時(shí)操作怪与,很容易看到timer會(huì)有很大的誤差,這說明在線程很閑的時(shí)候使用NSTiemr是比較傲你準(zhǔn)確的缅疟,當(dāng)線程很忙碌時(shí)候會(huì)有較大的誤差分别。系統(tǒng)還有一個(gè)CADisplayLink,也可以實(shí)現(xiàn)定時(shí)效果,它是一個(gè)和屏幕的刷新率一樣的定時(shí)器存淫。如果在兩次屏幕刷新之間執(zhí)行一個(gè)耗時(shí)的任務(wù)耘斩,那其中就會(huì)有一個(gè)幀被跳過去,造成界面卡頓桅咆。另外GCD也可以實(shí)現(xiàn)定時(shí)器的效果括授,由于其和RunLoop沒有關(guān)聯(lián),所以有時(shí)候使用它會(huì)更加的準(zhǔn)確岩饼,這在最后會(huì)給予說明荚虚。

六、讓兩個(gè)后臺(tái)線程有依賴性的一種方式

給兩個(gè)后臺(tái)線程添加依賴可能有很多的方式籍茧,這里說明一種利用RunLoop實(shí)現(xiàn)的方式版述。原理很簡單,我們先讓一個(gè)線程工作硕糊,當(dāng)工作完成之后喚醒另外的一線程,通過上面對RunLoop的說明院水,相信大家很容易能夠理解這些代碼:

- (void)runLoopAddDependance{

self.runLoopThreadDidFinishFlag = NO;
NSLog(@"Start a New Run Loop Thread");
NSThread *runLoopThread = [[NSThread alloc] initWithTarget:self selector:@selector(handleRunLoopThreadTask) object:nil];
[runLoopThread start];

NSLog(@"Exit handleRunLoopThreadButtonTouchUpInside");
dispatch_async(dispatch_get_global_queue(0, 0), ^{


    while (!_runLoopThreadDidFinishFlag) {

        self.myThread = [NSThread currentThread];
        NSLog(@"Begin RunLoop");
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        NSPort *myPort = [NSPort port];
        [runLoop addPort:myPort forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        NSLog(@"End RunLoop");
        [self.myThread cancel];
        self.myThread = nil;

    }
});

 }
- (void)handleRunLoopThreadTask
{
NSLog(@"Enter Run Loop Thread");
for (NSInteger i = 0; i < 5; i ++) {
    NSLog(@"In Run Loop Thread, count = %ld", i);
    sleep(1);
}
#if 0
// 錯(cuò)誤示范
_runLoopThreadDidFinishFlag = YES;
// 這個(gè)時(shí)候并不能執(zhí)行線程完成之后的任務(wù)腊徙,因?yàn)镽un Loop所在的線程并不知道runLoopThreadDidFinishFlag被重新賦值简十。Run Loop這個(gè)時(shí)候沒有被任務(wù)事件源喚醒檬某。
// 正確的做法是使用 "selector"方法喚醒Run Loop。 即如下:
#endif
NSLog(@"Exit Normal Thread");
[self performSelector:@selector(tryOnMyThread) onThread:self.myThread withObject:nil waitUntilDone:NO];

// NSLog(@"Exit Run Loop Thread");
}

七螟蝙、NSURLConnection的執(zhí)行過程

在使用NSURLConnection時(shí)恢恼,我們會(huì)傳入一個(gè)Delegate,當(dāng)我們調(diào)用了[connection start]之后,這個(gè)Delegate會(huì)不停的收到事件的回調(diào)胰默。實(shí)際上场斑,start這個(gè)函數(shù)的內(nèi)部會(huì)獲取CurrentRunloop,然后在其中的DefaultMode中添加4個(gè)source牵署。如下圖所示漏隐,CFMultiplexerSource是負(fù)責(zé)各種Delegate回調(diào)的,CFHTTPCookieStorage是處理各種Cookie的奴迅。如下圖所示:

從中可以看出青责,當(dāng)開始網(wǎng)絡(luò)傳輸是,我們可以看到NSURLConnection創(chuàng)建了兩個(gè)新的線程:com.apple.NSURLConnectionLoader和com.apple.CFSocket.private取具。其中CFSocket是處理底層socket鏈接的脖隶。NSURLConnectionLoader這個(gè)線程內(nèi)部會(huì)使用RunLoop來接收底層socket的事件,并通過之前添加的source暇检,來通知(喚醒)上層的Delegate产阱。這樣我們就可以理解我們平時(shí)封裝網(wǎng)絡(luò)請求時(shí)候常見的下面邏輯了:

    while (!_isEndRequest)
{
    NSLog(@"entered run loop");
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}

NSLog(@"main finished,task be removed");

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
 {

  _isEndRequest = YES;

 }

這里我們就可以解決下面這些疑問了:

為什么這個(gè)While循環(huán)不停的執(zhí)行块仆,還需要使用一個(gè)RunLoop? 程序執(zhí)行一個(gè)while循環(huán)是不會(huì)耗費(fèi)很大性能的构蹬,我們這里的目的是想讓子線程在有任務(wù)的時(shí)候處理任務(wù),沒有任務(wù)的時(shí)候休眠悔据,來節(jié)約CPU的開支庄敛。

如果沒有為RunLoop添加item,那么它就會(huì)立即退出,這里的item呢? 其實(shí)系統(tǒng)已經(jīng)給我們默認(rèn)添加了4個(gè)source了蜜暑。

既然[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];讓線程在這里停下來铐姚,那么為什么這個(gè)循環(huán)會(huì)持續(xù)的執(zhí)行呢?因?yàn)檫@個(gè)一直在處理任務(wù)肛捍,并且接受系統(tǒng)對這個(gè)Delegate的回調(diào)隐绵,也就是這個(gè)回調(diào)喚醒了這個(gè)線程,讓它在這里循環(huán)拙毫。

八依许、AFNetWorking中是如何使用RunLoop的?

在AFN中AFURLConnectionOperation是基于NSURLConnection構(gòu)建的,其希望能夠在后臺(tái)線程來接收Delegate的回調(diào)缀蹄。為此AFN創(chuàng)建了一個(gè)線程,然后在里面開啟了一個(gè)RunLoop峭跳,然后添加item

+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
    [[NSThread currentThread] setName:@"AFNetworking"];
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
    [runLoop run];
}

}

+ (NSThread *)networkRequestThread {
    static NSThread *_networkRequestThread = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{ 
        _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
        [_networkRequestThread start];
    });
    return _networkRequestThread;
}

這里這個(gè)NSMachPort的作用和上文中的一樣膘婶,就是讓線程不至于在很快死亡,然后RunLoop不至于退出(如果要使用這個(gè)MachPort的話蛀醉,調(diào)用者需要持有這個(gè)NSMachPort悬襟,然后在外部線程通過這個(gè)port發(fā)送信息到這個(gè)loop內(nèi)部,它這里沒有這么做)。然后和上面的做法相似拯刁,在需要后臺(tái)執(zhí)行這個(gè)任務(wù)的時(shí)候脊岳,會(huì)通過調(diào)用:[NSObject performSelector:onThread:..]來將這個(gè)任務(wù)扔給后臺(tái)線程的RunLoop中來執(zhí)行。

- (void)start {
[self.lock lock];
if ([self isCancelled]) {
    [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
} else if ([self isReady]) {
    self.state = AFOperationExecutingState;
    [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
}
[self.lock unlock];
}

GCD定時(shí)器的實(shí)現(xiàn)

 - (void)gcdTimer{
// get the queue
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// creat timer
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// config the timer (starting time垛玻,interval)
// set begining time
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
// set the interval
uint64_t interver = (uint64_t)(1.0 * NSEC_PER_SEC);
dispatch_source_set_timer(self.timer, start, interver, 0.0);
dispatch_source_set_event_handler(self.timer, ^{
    // the tarsk needed to be processed async
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (int i = 0; i < 100000; i++) {
            NSLog(@"gcdTimer");
        }
    });
});
dispatch_resume(self.timer);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末割捅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子帚桩,更是在濱河造成了極大的恐慌亿驾,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件账嚎,死亡現(xiàn)場離奇詭異莫瞬,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)醉锄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門乏悄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人恳不,你說我怎么就攤上這事檩小。” “怎么了烟勋?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵规求,是天一觀的道長。 經(jīng)常有香客問我卵惦,道長阻肿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任沮尿,我火速辦了婚禮丛塌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘畜疾。我一直安慰自己赴邻,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布啡捶。 她就那樣靜靜地躺著姥敛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瞎暑。 梳的紋絲不亂的頭發(fā)上彤敛,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天与帆,我揣著相機(jī)與錄音,去河邊找鬼墨榄。 笑死玄糟,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的渠概。 我是一名探鬼主播茶凳,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼嫂拴,長吁一口氣:“原來是場噩夢啊……” “哼播揪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起筒狠,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤猪狈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后辩恼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體雇庙,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年灶伊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了疆前。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡聘萨,死狀恐怖竹椒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情米辐,我是刑警寧澤胸完,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站翘贮,受9級特大地震影響赊窥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜狸页,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一锨能、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧芍耘,春花似錦址遇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至窃页,卻和暖如春跺株,著一層夾襖步出監(jiān)牢的瞬間复濒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工乒省, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留巧颈,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓袖扛,卻偏偏與公主長得像砸泛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子蛆封,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

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