Runloop學(xué)習(xí)總結(jié)

什么是Runloop

· 一般來(lái)講住闯,一個(gè)線程一次只能執(zhí)行一個(gè)任務(wù)瓜浸,執(zhí)行完成后線程就會(huì)退出澳淑。如果我們需要一個(gè)機(jī)制,讓線程能隨時(shí)處理事件但并不退出插佛,通常的代碼邏輯是這樣的:

function loop() {
    initialize();
    do {
        var message = get_next_message();
        process_message(message);
    } while (message != quit);
}

· Runloop類似于一個(gè)while循環(huán)杠巡,循環(huán)執(zhí)行代碼,保持程序的持續(xù)運(yùn)行雇寇。
· RunLoop 實(shí)際上就是一個(gè)對(duì)象氢拥,這個(gè)對(duì)象管理了其需要處理的事件和消息,并提供了一個(gè)入口函數(shù)來(lái)執(zhí)行上面 Event Loop 的邏輯谢床。線程執(zhí)行了這個(gè)函數(shù)后兄一,就會(huì)一直處于這個(gè)函數(shù)內(nèi)部 "接受消息->等待->處理" 的循環(huán)中,直到這個(gè)循環(huán)結(jié)束(比如傳入 quit 的消息)识腿,函數(shù)返回出革。

· 在iOS的工程的main.m文件中我們可以看到這樣的代碼:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

UIApplicationMain函數(shù)內(nèi)部就啟動(dòng)了一個(gè)Runloop,使App一直運(yùn)行渡讼,這個(gè)默認(rèn)開(kāi)啟的Runloop默認(rèn)和主線程關(guān)聯(lián)起來(lái)骂束。

· 新建一個(gè)工程,在storyboard上加上按鈕成箫,運(yùn)行


Paste_Image.png

結(jié)果如下:

Paste_Image.png

從Xcode左上角看的出來(lái)程序一直在運(yùn)行


Paste_Image.png

當(dāng)把代碼改為:

int main(int argc, char * argv[]) {
    @autoreleasepool {
//        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        return 0;
    }
}

main函數(shù)直接返回0展箱,AppDelegate里面的方法沒(méi)有執(zhí)行,然后程序就就退出了蹬昌。

Paste_Image.png

再把代碼修改如下:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        NSLog(@"%@", @"這里會(huì)打印");
        int result = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        NSLog(@"%@", @"這里不會(huì)打印");
        return result;
    }
}

運(yùn)行結(jié)果如下:

Paste_Image.png

程序執(zhí)行了UIApplicationMain后開(kāi)啟了默認(rèn)的Runloop混驰,一直循環(huán)15行,所以16行代碼永遠(yuǎn)沒(méi)有執(zhí)行皂贩。
Runloop可以看作下面的偽代碼:

int main(int argc, char * argv[]) {
   BOOL AppIsRunning = YES;
   while (AppIsRunning) {
        id whoWakesMe = SleepForWakingUp();
        id event = GetEvent(whoWakesMe);
        HandleEvent(event);
    }
    return 0;
}

Runloop有什么用處

1栖榨、使程序一直運(yùn)行接受用戶輸入
2、決定程序在何時(shí)應(yīng)該處理哪些Event
3明刷、調(diào)用解耦(對(duì)于編程經(jīng)驗(yàn)為0的完全沒(méi)搞懂這個(gè)意思,解釋為Message Queue)
4婴栽、節(jié)省CPU時(shí)間


<br />

Runloop的機(jī)制

(套用sunnnyxx 在視頻中提供的資料)

Paste_Image.png
Paste_Image.png
Paste_Image.png

Runloop事件隊(duì)列

Paste_Image.png
Paste_Image.png
Paste_Image.png

RunLoop的掛起與喚醒
從偽代碼可以看出

  • 制定用于喚醒的mach_port端口
  • 調(diào)用mach_msg
  • 監(jiān)聽(tīng)喚醒端口,被喚醒前,系統(tǒng)內(nèi)核將這個(gè)線程掛起,停留在mach_msg_trap
  • 由另外一個(gè)線程(或另一個(gè)進(jìn)程中的某個(gè)線程)向內(nèi)核發(fā)送這個(gè)端口的msg后,trap狀態(tài)被喚醒,RunLoop繼續(xù)開(kāi)始干活

<br />
Runloop對(duì)象
1.iOS中有2tAPI來(lái)訪問(wèn)和使用RunLoop
-Foundation 框架
NSRunLoop
-Core Foundation
CFRunLoopRef
2.NSRunLoop和CFRunLoopRef都代表著RunLoop對(duì)象
3.NSRunLoop是基于CFRunLoopRef的一層OC包裝, 所以要了解RunLoop內(nèi)部結(jié)構(gòu),需要多研究CFRunLoopRef層面的API (Core Foundation 層面)

a 主線程的runloop自動(dòng)創(chuàng)建,子線程的runloop默認(rèn)不創(chuàng)建(在子線程中調(diào)用NSRunLoop *runloop = [NSRunLoop currentRunLoop];獲取RunLoop對(duì)象的時(shí)候辈末,就會(huì)創(chuàng)建RunLoop)愚争;
b runloop退出的條件:app退出;線程關(guān)閉挤聘;設(shè)置最大時(shí)間到期轰枝;modeItem為空;
c 同一時(shí)間一個(gè)runloop只能在一個(gè)mode组去,切換mode只能退出runloop狸膏,再重進(jìn)指定mode(隔離modeItems使之互不干擾);
d 一個(gè)item可以加到不同mode添怔;一個(gè)mode被標(biāo)記到commonModes里(這樣runloop不用切換mode)湾戳。

<br />Source是RunLoop的數(shù)據(jù)源抽象類(protocol)

RunLoop定義了兩個(gè)Version的Source:
1、Source0:處理App內(nèi)部事件广料、App自己負(fù)責(zé)管理(觸發(fā))砾脑,如UIEvent、CFSocket
2艾杏、Source1:由RunLoop和內(nèi)核管理韧衣,Mach port驅(qū)動(dòng),如CFMachPort购桑、CFMessagePort
如有需要畅铭,可從中選擇一種來(lái)實(shí)現(xiàn)自己的Source
上一條基本不會(huì)發(fā)生

<br />RunLoopTimer的封裝

// 創(chuàng)建但是不會(huì)加入當(dāng)前 Runloop
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;

// 創(chuàng)建但是加入當(dāng)前 Runloop 的 NSDefaultRunLoopMode 并執(zhí)行
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;

- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;

+ (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel;
- (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode;

<br />CFRunLoopObserver
向外部報(bào)告RunLoop當(dāng)前狀態(tài)的更改,框架中很多機(jī)制都由RunLoopObserver觸發(fā)勃蜘,如CAAnimation

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),
    kCFRunLoopBeforeTimers = (1UL << 1),
    kCFRunLoopBeforeSources = (1UL << 2),
    kCFRunLoopBeforeWaiting = (1UL << 5),
    kCFRunLoopAfterWaiting = (1UL << 6),
    kCFRunLoopExit = (1UL << 7),
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

UIKit通過(guò)RunLoopObserver在RunLoop兩次Sleep間對(duì)AutoreleasePool進(jìn)行Pop和Push硕噩,將這次Loop中產(chǎn)生的Autorelease對(duì)象釋放
Runloop的寄生于線程:一個(gè)線程只能有唯一對(duì)應(yīng)的runloop;但這個(gè)根runloop里可以嵌套子runloops缭贡;
自動(dòng)釋放池寄生于Runloop:程序啟動(dòng)后炉擅,主線程注冊(cè)了兩個(gè)Observer監(jiān)聽(tīng)runloop的進(jìn)出與睡覺(jué)。一個(gè)最高優(yōu)先級(jí)OB監(jiān)測(cè)Entry狀態(tài)阳惹;一個(gè)最低優(yōu)先級(jí)OB監(jiān)聽(tīng)BeforeWaiting狀態(tài)和Exit狀態(tài)谍失。
線程(創(chuàng)建)-->runloop將進(jìn)入-->最高優(yōu)先級(jí)OB創(chuàng)建釋放池-->runloop將睡-->最低優(yōu)先級(jí)OB銷毀舊池創(chuàng)建新池-->runloop將退出-->最低優(yōu)先級(jí)OB銷毀新池-->線程(銷毀)

Paste_Image.png

<br />CFRunLoopMode

  • RunLoop在同一段時(shí)間只能且必須在一種特定Mode下Run
  • 更換Mode時(shí),需要停止當(dāng)前Loop莹汤,然后重啟新Loop
  • Mode是iOS App滑動(dòng)順暢的關(guān)鍵
  • 可以定制自己的Mode
// 默認(rèn)狀態(tài)快鱼、空閑狀態(tài)
NSDefaultRunLoopMode
// 滑動(dòng)ScrollView時(shí)
UITrackingRunLoopMode
// 私有,App啟動(dòng)時(shí)
UIInitializationRunLoopMode
// Mode集合纲岭,可以理解為 NSDefaultRunLoopMode 和 UITrackingRunLoopMode 的集合
NSRunLoopCommonModes
Runloop與GCD任務(wù):

當(dāng)調(diào)用 dispatch_async(dispatch_get_main_queue(), block) 時(shí),libDispatch 會(huì)向主線程的 RunLoop 發(fā)送消息抹竹,RunLoop會(huì)被喚醒,并從消息中取得這個(gè) block荒勇,并在回調(diào)里執(zhí)行這個(gè) block柒莉。Runloop只處理主線程的block,dispatch 到其他線程仍然是由 libDispatch 處理的沽翔。

關(guān)于網(wǎng)絡(luò)請(qǐng)求

iOS 中兢孝,關(guān)于網(wǎng)絡(luò)請(qǐng)求的接口自下至上有如下幾層:

CFSocket
CFNetwork       ->ASIHttpRequest
NSURLConnection ->AFNetworking
NSURLSession    ->AFNetworking2, Alamofire

1.CFSocket 是最底層的接口,只負(fù)責(zé) socket 通信仅偎。
2.CFNetwork 是基于 CFSocket 等接口的上層封裝跨蟹,ASIHttpRequest 工作于這一層。
3.NSURLConnection 是基于 CFNetwork 的更高層的封裝橘沥,提供面向?qū)ο蟮慕涌诖靶珹FNetworking 工作于這一層。
4.NSURLSession 是 iOS7 中新增的接口座咆,表面上是和 NSURLConnection 并列的痢艺,但底層仍然用到了 NSURLConnection 的部分功能 (比如 com.apple.NSURLConnectionLoader 線程)仓洼,AFNetworking2 和 Alamofire 工作于這一層。

下面主要介紹下 NSURLConnection 的工作過(guò)程堤舒。

通常使用 NSURLConnection 時(shí)色建,你會(huì)傳入一個(gè) Delegate,當(dāng)調(diào)用了 [connection start] 后舌缤,這個(gè) Delegate 就會(huì)不停收到事件回調(diào)箕戳。實(shí)際上,start 這個(gè)函數(shù)的內(nèi)部會(huì)會(huì)獲取 CurrentRunLoop国撵,然后在其中的 DefaultMode 添加了4個(gè) Source0 (即需要手動(dòng)觸發(fā)的Source)陵吸。CFMultiplexerSource 是負(fù)責(zé)各種 Delegate 回調(diào)的,CFHTTPCookieStorage 是處理各種 Cookie 的介牙。

當(dāng)開(kāi)始網(wǎng)絡(luò)傳輸時(shí)壮虫,我們可以看到 NSURLConnection 創(chuàng)建了兩個(gè)新線程:com.apple.NSURLConnectionLoader 和 com.apple.CFSocket.private。其中 CFSocket 線程是處理底層 socket 連接的耻瑟。NSURLConnectionLoader 這個(gè)線程內(nèi)部會(huì)使用 RunLoop 來(lái)接收底層 socket 的事件旨指,并通過(guò)之前添加的 Source0 通知到上層的 Delegate。



NSURLConnectionLoader 中的 RunLoop 通過(guò)一些基于 mach port 的 Source 接收來(lái)自底層 CFSocket 的通知喳整。當(dāng)收到通知后谆构,其會(huì)在合適的時(shí)機(jī)向 CFMultiplexerSource 等 Source0 發(fā)送通知,同時(shí)喚醒 Delegate 線程的 RunLoop 來(lái)讓其處理這些通知框都。CFMultiplexerSource 會(huì)在 Delegate 線程的 RunLoop 對(duì) Delegate 執(zhí)行實(shí)際的回調(diào)搬素。


Runloop實(shí)驗(yàn)

實(shí)驗(yàn)一
- (IBAction)buttonDidClick:(id)sender {
    [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
}
     
- (void)timerTest {
    NSLog(@"%s", __func__);
}

輸出結(jié)果

Paste_Image.png
實(shí)驗(yàn)二

把代碼改成如下,輸入結(jié)果一樣

NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

如果把[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];屏蔽魏保,會(huì)發(fā)現(xiàn)沒(méi)有打印東西熬尺,因?yàn)?code>timerWithTimeInterval這個(gè)方法只是創(chuàng)建了并沒(méi)有加入Runloop

實(shí)驗(yàn)三 有scrollView的情況下使用Timer

在實(shí)驗(yàn)二的基礎(chǔ)上,在vc中加一個(gè)textView谓罗,run起來(lái)粱哼,模擬器界面如下:

Paste_Image.png

點(diǎn)擊按鈕,然后滾動(dòng)scrollView檩咱,在停止?jié)L動(dòng)揭措,打印結(jié)果

Paste_Image.png

可以看的出來(lái)滾動(dòng)的時(shí)間段,timer并沒(méi)有效果刻蚯,那是因?yàn)闈L動(dòng)的時(shí)候主線程Runloop已經(jīng)切換mode為UITrackingRunLoopMode绊含,Runloop只能指定一個(gè)mode,而timer只是加在NSDefaultRunLoopMode炊汹,所以發(fā)生滾動(dòng)的時(shí)候躬充,Runloop并不會(huì)響應(yīng)timer;當(dāng)松開(kāi)手的時(shí)候Runloop切換回NSDefaultRunLoopMode,timer就重新起作用充甚。

當(dāng)我們把timer的mode修改為NSRunLoopCommonModes以政,此時(shí)滾動(dòng)scrollView的同時(shí)也能響應(yīng)timer:

NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

<br />

實(shí)驗(yàn)四 CFRunLoopSourseRef的實(shí)驗(yàn)

我們?cè)赽utton的響應(yīng)注釋,然后打個(gè)斷點(diǎn)津坑,run后點(diǎn)擊button會(huì)發(fā)現(xiàn)如下類似這種UIEvent是屬于Souce0

Paste_Image.png

<br />

實(shí)驗(yàn)五 CFRunLoopObserverRef的實(shí)驗(yàn)

- (void)createObserver {
    // 創(chuàng)建監(jiān)聽(tīng)者對(duì)象
    // rl: RunLoop
    // observer: 監(jiān)聽(tīng)者對(duì)象
    // mode: Runloop所在的mode
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        NSLog(@"observer--------%lu", activity);
    });
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    CFRelease(observer);
}
Paste_Image.png

根據(jù)CFRunLoopActivity枚舉妙蔗,我們可以看出Runloop的狀態(tài)變化
1:即將進(jìn)入Runloop-> 2:即將處理NSTimer-> 4:即將處理Souce0 -> 32:即將進(jìn)入休眠 -> 64:從休眠仲喚醒

/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),
    kCFRunLoopBeforeTimers = (1UL << 1),
    kCFRunLoopBeforeSources = (1UL << 2),
    kCFRunLoopBeforeWaiting = (1UL << 5),
    kCFRunLoopAfterWaiting = (1UL << 6),
    kCFRunLoopExit = (1UL << 7),
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

實(shí)驗(yàn)更新

代碼:

- (IBAction)buttonDidClick:(id)sender {
    NSLog(@"%s", __func__);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        _myThread = [NSThread currentThread];
        [[NSRunLoop currentRunLoop] run];
        NSLog(@"%@",  @"+++++");
    });
}

- (void)timerTest {
    NSLog(@"%s", __func__);
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self performSelector:@selector(myThreadTest) onThread:_myThread withObject:nil waitUntilDone:NO];
}

- (void)myThreadTest {
    NSLog(@"%s", __FUNCTION__);
}

點(diǎn)擊按鈕后打印出+++++,然后點(diǎn)擊屏幕空白處- (void)myThreadTest并沒(méi)有觸發(fā)疆瑰。

Paste_Image.png

這是因?yàn)開(kāi)myThread中的Runloop只run了一次就退出了,從而子線程沒(méi)有監(jiān)聽(tīng)到屏幕的點(diǎn)擊事件昙啄。只run一次的原因首先看這張圖

Paste_Image.png

代碼中只是讓子線程的運(yùn)行循環(huán)run了一次穆役,并沒(méi)有加入實(shí)質(zhì)的source、port梳凛、Observer或者timer耿币,Runloop直接跑一次直接退出了,導(dǎo)致點(diǎn)擊時(shí)間沒(méi)有Runloop來(lái)響應(yīng)韧拒。

要響應(yīng)- (void)myThreadTest必須要子線程的Runloop保持駐留狀態(tài)淹接,給Runloop添加一個(gè)port讓其保持駐留,此時(shí)我們點(diǎn)擊button之后再點(diǎn)擊屏幕空白處可以看到打印出來(lái)的日志叛溢,可以看的出來(lái)點(diǎn)擊事件已經(jīng)起效了塑悼,并且+++++也沒(méi)有打印出來(lái),那是因?yàn)樽泳€程的運(yùn)行循環(huán)已經(jīng)駐留楷掉,循環(huán)外面的代碼就執(zhí)行不到厢蒜。

- (IBAction)buttonDidClick:(id)sender {
    NSLog(@"%s", __func__);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        _myThread = [NSThread currentThread];
        [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] run];
        NSLog(@"++++");
    });
}
Paste_Image.png

Runloop使用

AFNetworking中RunLoop的創(chuàng)建
+ (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;
}
利用Runloop有話UITableView

因?yàn)閁ITableView滾動(dòng)的時(shí)候主線程Runloop的mode切換為UITrackingRunLoopMode,當(dāng)停止?jié)L動(dòng)的時(shí)候會(huì)切回NSDefaultRunLoopMode烹植,從而可以減輕UITableView的卡頓。

    UIImage *downloadedImage = ...;
    [self.avatarImageView performSelector:@selector(setImage:)
                               withObject:downloadedImage
                               afterDelay:0
                                  inModes:@[NSDefaultRunLoopMode]];

參考資料:
http://blog.ibireme.com/2015/05/18/runloop/
http://www.reibang.com/p/37ab0397fec7
https://yun.baidu.com/share/link?shareid=2268593032&uk=2885973690

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市创南,隨后出現(xiàn)的幾起案子赃梧,更是在濱河造成了極大的恐慌,老刑警劉巖墩虹,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘱巾,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡败晴,警方通過(guò)查閱死者的電腦和手機(jī)浓冒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)尖坤,“玉大人稳懒,你說(shuō)我怎么就攤上這事。” “怎么了场梆?”我有些...
    開(kāi)封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵墅冷,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我或油,道長(zhǎng)寞忿,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任顶岸,我火速辦了婚禮腔彰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘辖佣。我一直安慰自己霹抛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布卷谈。 她就那樣靜靜地躺著杯拐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪世蔗。 梳的紋絲不亂的頭發(fā)上端逼,一...
    開(kāi)封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音污淋,去河邊找鬼顶滩。 笑死,一個(gè)胖子當(dāng)著我的面吹牛芙沥,可吹牛的內(nèi)容都是我干的诲祸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼而昨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼救氯!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起歌憨,我...
    開(kāi)封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤着憨,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后务嫡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體甲抖,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年心铃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了准谚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡去扣,死狀恐怖柱衔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤唆铐,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布哲戚,位于F島的核電站,受9級(jí)特大地震影響艾岂,放射性物質(zhì)發(fā)生泄漏顺少。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一王浴、第九天 我趴在偏房一處隱蔽的房頂上張望脆炎。 院中可真熱鬧,春花似錦氓辣、人聲如沸腕窥。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至癞松,卻和暖如春爽撒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背响蓉。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工硕勿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人枫甲。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓源武,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親想幻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子粱栖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

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

  • 通過(guò)閱讀YY大神的博客深入理解RunLoop還有觀看了孫源大大@sunnyxx錄制的RunLoop視頻,總算對(duì)Ru...
    巫師學(xué)徒閱讀 315評(píng)論 0 0
  • 什么是RunLoop 從字面上看脏毯,就是運(yùn)行循環(huán)闹究,跑圈 其實(shí)它內(nèi)部就是do-while循環(huán),在這個(gè)循環(huán)內(nèi)部不斷地處理...
    zhazha閱讀 1,433評(píng)論 1 7
  • Runloop是iOS和OSX開(kāi)發(fā)中非呈车辏基礎(chǔ)的一個(gè)概念渣淤,從概念開(kāi)始學(xué)習(xí)。 RunLoop的概念 -般說(shuō)吉嫩,一個(gè)線程一...
    小貓仔閱讀 999評(píng)論 0 1
  • 隨著年齡的增長(zhǎng)价认,在愛(ài)情中,最終和我們?cè)谝黄鸬耐⒉皇钱?dāng)初我們愛(ài)的死去活來(lái)的人自娩,取而代之的是那位愿意陪伴在我們身邊...
    琛筱閱讀 277評(píng)論 0 1
  • 步驟: 一用踩、先打形,畫出大概輪廓。(用筆要輕捶箱,盡量用直線畫線智什,先不要糾結(jié)細(xì)節(jié)。) 二丁屎、根據(jù)大形畫出花的具體輪廓荠锭,運(yùn)...
    南方小花CX閱讀 914評(píng)論 0 4