iOS RunLoop由淺入深

Event Loop

Event Loop事件循環(huán)機(jī)制,如javascript的事件循環(huán)裹驰,以及依賴其的nodejs都是采用的異步事件循環(huán)機(jī)制隧熙。

對于上述兩者,都是基于多線程幻林,但是都是單線程執(zhí)行任務(wù)代碼贞盯,其依賴的就是Event Loop事件循環(huán)機(jī)制,通過事件隊列注冊事件及事件的觀察者沪饺,事件的執(zhí)行交由其他線程去執(zhí)行(如I/O操作邻悬,網(wǎng)絡(luò)請求等),nodejs采用的是libuv異步I/O線程池庫随闽;對于非異步I/O操作,如setTimeOut setInterval等肝谭,都是基于事件循環(huán)查詢(每次事件處理完成后進(jìn)入下一次事件循環(huán)時都會查看時間是否已到達(dá)掘宪,并且是任務(wù)是插入到任務(wù)隊列尾部蛾扇,因此存在誤差,不過也可采用process.netxTick會將事件插入到事件循環(huán)前解析執(zhí)行魏滚,且可嵌套執(zhí)行)镀首;

image.png

image.png

  • 定時器:本階段執(zhí)行已經(jīng)被 setTimeout()setInterval() 的調(diào)度回調(diào)函數(shù)。
  • 待定回調(diào):執(zhí)行延遲到下一個循環(huán)迭代的 I/O 回調(diào)鼠次。
  • idle, prepare:僅系統(tǒng)內(nèi)部使用更哄。
  • 輪詢:檢索新的 I/O 事件;執(zhí)行與 I/O 相關(guān)的回調(diào)(幾乎所有情況下,除了關(guān)閉的回調(diào)函數(shù)腥寇,那些由計時器和 setImmediate() 調(diào)度的之外)成翩,其余情況 node 將在適當(dāng)?shù)臅r候在此阻塞。
  • 檢測setImmediate() 回調(diào)函數(shù)在這里執(zhí)行赦役。
  • 關(guān)閉的回調(diào)函數(shù):一些關(guān)閉的回調(diào)函數(shù)麻敌,如:socket.on('close', ...)

JavaScript單線程異步的背后——事件循環(huán)機(jī)制

《深入淺出Nodejs》

Nodejs Event Loop

JavaScript 運行機(jī)制詳解:再談Event Loop

Mach Port

這個在《unix進(jìn)程間通信》中已闡述掂摔,在這不過多闡述术羔;

RunLoop

RunLoop就像其名字一樣,就是運行循環(huán)乙漓,核心就是事件循環(huán)+mach port级历,利用事件循環(huán)注冊觀察相應(yīng)的事件,若無事件處理叭披,線程就去睡眠等待內(nèi)核事件觸發(fā)或者通過手動喚醒寥殖,不停地循環(huán)處理各種事件(如timer source0 source1事件以及dispatch分發(fā)的func block等);

注:以下代碼分析基于CF-1151.16源碼趋观;

runloop與線程的關(guān)系

直接上結(jié)論:runloop與線程是一一對應(yīng)的扛禽,且對于主線程是默認(rèn)開啟的,對于其他線程皱坛,需要通過手動開啟编曼,且只能通過蘋果對外的接口獲取線程相應(yīng)的CFRunLoopRef對象:

CFRunLoopRef CFRunLoopGetMain(void);
CFRunLoopRef CFRunLoopGetCurrent(void);

原理就是:蘋果維護(hù)了一個全局的字典對象,若字典中不存在線程對應(yīng)的runloop對象就會創(chuàng)建并賦值剩辟,并且還利用線程私有數(shù)據(jù)(數(shù)組)存儲了指定__CFTSDKeyRunLoop當(dāng)前線程的CFRunLoopRef對象(同時也關(guān)聯(lián)了runloop對象銷毀的回調(diào)掐场,用于線程退出銷毀);策略是:優(yōu)先從線程私有數(shù)據(jù)數(shù)組中獲取贩猎,若獲取不到就從全局字典對象中獲取熊户,若無則去創(chuàng)建;

runloop對象結(jié)構(gòu)分析

CoreFoundation中關(guān)于runloop的五個類:

CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef

其中CFRunLoopRef對象結(jié)構(gòu)體如下:

//CFRunLoop結(jié)構(gòu)體結(jié)構(gòu)
struct __CFRunLoop
{
    CFRuntimeBase _base;
    pthread_mutex_t _lock; /* locked for accessing mode list */
    __CFPort _wakeUpPort;  // used for CFRunLoopWakeUp
    Boolean _unused;
    volatile _per_run_data *_perRunData; // reset for runs of the run loop
    pthread_t _pthread;//綁定的線程pthread_t
    uint32_t _winthread;
    CFMutableSetRef _commonModes;//通用的mode set集合
    CFMutableSetRef _commonModeItems;//通用mode的itme集合
    CFRunLoopModeRef _currentMode;//當(dāng)前mode
    CFMutableSetRef _modes;//所有的mode
    struct _block_item *_blocks_head;//添加的block任務(wù)吭服,與dispatch分發(fā)的block處理不同
    struct _block_item *_blocks_tail;
    CFAbsoluteTime _runTime;
    CFAbsoluteTime _sleepTime;
    CFTypeRef _counterpart;
};

runloop對象的結(jié)構(gòu)體為__CFRunLoop嚷堡,其存儲著runloop相關(guān)的鎖保證線程安全(注意下NSRunLoop不是線程安全的),喚醒端口(用于CFRunLoopWakeUp外部接口調(diào)用艇棕,主要是source0)蝌戒,綁定的線程串塑,mode各種集合(下面會重點闡述),block處理任務(wù)(通過CFRunLoopPerformBlock接口注冊的)以及記錄需要的相關(guān)信息(如運行時間_runTime _sleepTime)等北苟;

CFRunLoopModeRef

image.png

CFRunLoopRef對象中包含了若干Mode桩匪,Mode對象的數(shù)據(jù)結(jié)構(gòu)如下:

struct __CFRunLoopMode
{
    CFRuntimeBase _base;
    pthread_mutex_t _lock; /* must have the run loop locked before locking this */
    CFStringRef _name;//mode名稱
    Boolean _stopped;
    char _padding[3];
    CFMutableSetRef _sources0;//source0對象
    CFMutableSetRef _sources1;//source1對象
    CFMutableArrayRef _observers;//observer對象
    CFMutableArrayRef _timers;//定時器
    CFMutableDictionaryRef _portToV1SourceMap;
    __CFPortSet _portSet;//需要監(jiān)聽的所有mach port集合
    CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
    dispatch_source_t _timerSource;
    dispatch_queue_t _queue;
    Boolean _timerFired; // set to true by the source when a timer has fired
    Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
    //mk_timer由mach port實現(xiàn)<https://opensource.apple.com/source/xnu/xnu-3789.51.2/osfmk/kern/mk_timer.c>
    mach_port_t _timerPort;
    Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
    DWORD _msgQMask;
    void (*_msgPump)(void);
#endif
    uint64_t _timerSoftDeadline; /* TSR */
    uint64_t _timerHardDeadline; /* TSR */
};

CFRunLoopModeRef對象未對外暴露,可通過CFRunLoopCopyAllModesCFRunLoopCopyCurrentMode獲取所有runloop相關(guān)的Mode友鼻,通過CFRunLoopAddCommonMode添加Mode傻昙;

runloop可添加多個Mode,但只能指定一個Mode模式運行(默認(rèn)是kCFRunLoopDefaultMode)彩扔,且需要退出當(dāng)前重新指定運行才能生效妆档;每個Mode中可添加若干sourcetimer借杰、observer过吻;

對于CoreFoundation中的CFRunLoop蘋果只提供了兩種默認(rèn)模式kCFRunLoopDefaultModekCFRunLoopCommonModes其中kCFRunLoopCommonModes只是操作common標(biāo)記的字符串蔗衡,用于向所有現(xiàn)有的Modes中添加相應(yīng)的觀察者纤虽,不是一種具體的Mode,不能直接用于CFRunLoopRunInMode調(diào)用運行绞惦;但上層NSRunLoopMode封裝了一些相應(yīng)的Mode逼纸,如

  • NSDefaultRunLoopModekCFRunLoopDefaultMode默認(rèn)的模式
  • NSEventTrackingRunLoopMode模態(tài)跟蹤事件時,例如鼠標(biāo)拖動循環(huán)济蝉,應(yīng)將運行循環(huán)設(shè)置為此模式杰刽;
  • NSModalPanelRunLoopMode運行等待模態(tài)窗口(如NSSavePanel NSOpenPanel)的輸入時指定;
  • UITrackingRunLoopMode運行控件追蹤時指定王滤,如UIScrollView滑動時(這個系統(tǒng)會默認(rèn)自動切換到此模式)

iOS應(yīng)用啟動時系統(tǒng)默認(rèn)注冊了5個Mode:

  • kCFRunLoopDefaultMode: App的默認(rèn) Mode贺嫂,通常主線程是在這個 Mode下運行的;
  • UITrackingRunLoopMode: 界面跟蹤 Mode雁乡,用于 ScrollView 追蹤觸摸滑動第喳,保證界面滑動時不受其他 Mode 影響;
  • UIInitializationRunLoopMode: 在剛啟動 App 時第進(jìn)入的第一個 Mode踱稍,啟動完成后就不再使用曲饱;
  • GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode,通常用不到珠月;
  • kCFRunLoopCommonModes: 這是一個占位的 Mode扩淀,沒有實際作用;

CFRunLoopSourceRef

CFRunLoopSourceRef對象的數(shù)據(jù)結(jié)構(gòu)如下:

struct __CFRunLoopSource
{
    CFRuntimeBase _base;
    uint32_t _bits;
    pthread_mutex_t _lock;
    CFIndex _order; /* immutable */
    CFMutableBagRef _runLoops;
    union {
        CFRunLoopSourceContext version0;  /* immutable, except invalidation */
        CFRunLoopSourceContext1 version1; /* immutable, except invalidation */
    } _context;
};

typedef struct {
    CFIndex version;
    void *  info;
    const void *(*retain)(const void *info);
    void    (*release)(const void *info);
    CFStringRef (*copyDescription)(const void *info);
    Boolean (*equal)(const void *info1, const void *info2);
    CFHashCode  (*hash)(const void *info);
    void    (*schedule)(void *info, CFRunLoopRef rl, CFRunLoopMode mode);
    void    (*cancel)(void *info, CFRunLoopRef rl, CFRunLoopMode mode);
    void    (*perform)(void *info);
} CFRunLoopSourceContext;

typedef struct {
    CFIndex version;
    void *  info;
    const void *(*retain)(const void *info);
    void    (*release)(const void *info);
    CFStringRef (*copyDescription)(const void *info);
    Boolean (*equal)(const void *info1, const void *info2);
    CFHashCode  (*hash)(const void *info);
    mach_port_t (*getPort)(void *info);
    void *  (*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info);
} CFRunLoopSourceContext1;

其中聯(lián)合體union中的versionx.version字段信息用于區(qū)分source0或者source1啤挎;

  • source0驻谆,結(jié)構(gòu)體中context上下文中包含了各種回調(diào)函數(shù),主要是perform回調(diào)函數(shù)(用于執(zhí)行添加到source0中的任務(wù)),當(dāng)調(diào)用CFRunLoopSourceSignal時會標(biāo)記__CFRunLoopSource中的_bits標(biāo)記位旺韭,然后調(diào)用CFRunLoopWakeUp來喚醒runloop再下一個循環(huán)中處理此回調(diào)氛谜;

    主要用于APP內(nèi)部事件,由APP負(fù)責(zé)管理觸發(fā)区端,如UIEvent事件;

  • source1澳腹,不同于source0執(zhí)行回調(diào)函數(shù)织盼,source1還需要指定mach port,用于監(jiān)聽系統(tǒng)內(nèi)核事件或其他線程發(fā)來的事件酱塔;

CFRunLoopTimer

struct __CFRunLoopTimer
{
    CFRuntimeBase _base;
    uint16_t _bits;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;
    CFMutableSetRef _rlModes;
    CFAbsoluteTime _nextFireDate;
    CFTimeInterval _interval;        /* immutable */
    CFTimeInterval _tolerance;       /* mutable */
    uint64_t _fireTSR;               /* TSR units */
    CFIndex _order;                  /* immutable */
    CFRunLoopTimerCallBack _callout; /* immutable */
    CFRunLoopTimerContext _context;  /* immutable, except invalidation */
};

CFRunLoopTimer結(jié)構(gòu)中包含了時間相關(guān)的變量沥邻,runlooptimer事件觸發(fā)都會去檢查當(dāng)前所有的timer時間點是否達(dá)到哀军,若達(dá)到則處理事件任務(wù)译柏;具體觸發(fā)時間事件主要包含兩種mk_timerdispatch source形式,兩者都是基于mach port但是觸發(fā)runloop并處理回調(diào)的處理方式不同接癌;

mk_timer是通過__CFRunLoopDoTimers來處理蕊玷,依賴于runloop來觸發(fā)時間回調(diào)函數(shù)邮利,因此基于此的NStimerperformSelector:withObject:afterDelay:(是對NSTimer的包裝),都需要runloop運行垃帅;

dispatch source(針對主隊列延届,其他隊列不是通過runloop來觸發(fā))是通過__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__中指定的外部函數(shù)_dispatch_main_queue_callback_4CF來處理,調(diào)用堆棧如下

image.png

dispatch_after指定主隊列的時間任務(wù)贸诚,是對dispatch_source的包裝方庭,對于存在UI的應(yīng)用默認(rèn)主隊列的runloop是開啟的,若是其他工具類應(yīng)用酱固,則需要手動開啟主隊列runloop械念,否則指定主隊列的dispatch source是無法生效的;

Timer有兩種實現(xiàn)方式分別是MK_Timer和GCD Timer,在runloop中Timer被轉(zhuǎn)為了一個存了觸發(fā)時間的列表运悲,這個觸發(fā)時間是一個絕對時間龄减,會按時間大小升序排序,在最小的時間被觸發(fā)后扇苞,Runloop會更新列表保證時間始終是升序排列欺殿。如果Runloop在某次運行中阻塞了很長時間,Timer的觸發(fā)會受到影響鳖敷。過期的時間點會被移除而不會去觸發(fā)脖苏。

具體的NSTimerGCD Timer實現(xiàn)剖析可參考從NSTimer的失效性談起(二):關(guān)于GCD Timer和libdispatch

不過在源碼中的USE_DISPATCH_SOURCE_FOR_TIMERS未生效,暫時未搞清問題定踱,待后續(xù)補(bǔ)充棍潘;

CFRunLoopObserver

struct __CFRunLoopObserver
{
    CFRuntimeBase _base;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;
    CFIndex _rlCount;
    CFOptionFlags _activities;          /* immutable */
    CFIndex _order;                     /* immutable */
    CFRunLoopObserverCallBack _callout; /* immutable */
    CFRunLoopObserverContext _context;  /* immutable, except invalidation */
};

CFRunLoopObserver觀察者對象指定了runloop相應(yīng)狀態(tài)變化(_activities指定需要觀察的類型)及狀態(tài)變化的回調(diào)指針_callout,具體觀察的選項包括:

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry         = (1UL << 0), // 即將進(jìn)入Loop
    kCFRunLoopBeforeTimers  = (1UL << 1), // 即將處理 Timer
    kCFRunLoopBeforeSources = (1UL << 2), // 即將處理 Source
    kCFRunLoopBeforeWaiting = (1UL << 5), // 即將進(jìn)入休眠
    kCFRunLoopAfterWaiting  = (1UL << 6), // 剛從休眠中喚醒
    kCFRunLoopExit          = (1UL << 7), // 即將退出Loop
};

RunLoop事件循環(huán)

image.png

image.png

runloop事件循環(huán)的邏輯如上圖,對于黃色的Block是通過CFRunLoopPerformBlock添加的block任務(wù)亦歉,若runloop任務(wù)處理完成后就會休眠等待source1恤浪、timer、或者手動被喚醒來繼續(xù)下一次循環(huán)處理任務(wù)肴楷;

RunLoop實踐

AutoReleasePool

主要是利用runloop observer觀察注冊的事件:kCFRunLoopEntry水由、kCFRunLoopBeforeWaitingkCFRunLoopExit赛蔫,分別用于autoreleasepool push/pop操作來創(chuàng)建/釋放內(nèi)存池砂客,并且保證自動內(nèi)存池創(chuàng)建優(yōu)先級其他回調(diào)之前,釋放內(nèi)存池在其他回調(diào)之后呵恢,進(jìn)而不會導(dǎo)致內(nèi)存泄露鞠值;

事件響應(yīng)/手勢識別

對于IOKit.framework生成的IOHIDEvent(如觸摸/鎖屏/靜音/傳感器加速等)會發(fā)送給SpringBoard接收,并通過mach port發(fā)送給注冊了相應(yīng)端口的source1應(yīng)用進(jìn)程渗钉,進(jìn)而觸發(fā)事件回調(diào)__IOHIDEventSystemClientQueueCallback彤恶,并通過_UIApplicationHandleEventQueue內(nèi)部注冊source0事件進(jìn)行事件應(yīng)用內(nèi)部分發(fā);

手勢識別就是將上面識別的手勢UIGestureRecognizer標(biāo)記為待處理鳄橘,并注冊了observer監(jiān)測BeforeWaiting事件声离,觸發(fā)回調(diào)來處理待處理的手勢;

界面更新

蘋果注冊了observer監(jiān)測BeforeWaiting/Eixt事件挥唠,當(dāng)事件發(fā)生時會將已提交到全局容器待界面繪制的任務(wù)執(zhí)行并更新UI抵恋,如果中間執(zhí)行大量邏輯計算的任務(wù)導(dǎo)致runloop遲遲不觸發(fā)ui更新的話,就會導(dǎo)致繪制ui的幀被丟棄即“丟幀”宝磨,進(jìn)而引發(fā)ui卡頓弧关,FaceBook推出的開源項目AsyncDisplayKit 就是防止主線程存在大量與ui不相關(guān)的任務(wù)處理(通過后臺線程處理)阻塞ui更新,來避免“丟幀”提升界面流暢度唤锉;

GCD

對于提交至主隊列的任務(wù)世囊,如dispatch_source timerdispatch_async窿祥,都是主隊列runloop中監(jiān)聽相對應(yīng)的mach port事件株憾,當(dāng)事件發(fā)生時(timer到期或dispatch_async添加到主隊列任務(wù)),libdispatch就會通過mach port端口向監(jiān)聽該端口的runloop發(fā)送喚醒消息晒衩,被喚醒的runloop觸發(fā)回調(diào)函數(shù)__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__嗤瞎,該回調(diào)函數(shù)內(nèi)部的_dispatch_main_queue_callback_4CF實際是由libdispatch定義處理的,即處理相應(yīng)的任務(wù)听系;

網(wǎng)絡(luò)請求

對于NSURLConnection實現(xiàn)原理如下圖贝奇,具體為:

image.png

當(dāng)開始網(wǎng)絡(luò)傳輸時,我們可以看到 NSURLConnection 創(chuàng)建了兩個新線程:com.apple.NSURLConnectionLoader 和 com.apple.CFSocket.private靠胜。其中 CFSocket 線程是處理底層 socket 連接的掉瞳。NSURLConnectionLoader 這個線程內(nèi)部會使用 RunLoop 來接收底層 socket 的事件毕源,并通過之前添加的 Source0 通知到上層的 Delegate。
NSURLConnectionLoader 中的 RunLoop 通過一些基于 mach port 的 Source 接收來自底層 CFSocket 的通知陕习。當(dāng)收到通知后霎褐,其會在合適的時機(jī)向 CFMultiplexerSource 等 Source0 發(fā)送通知,同時喚醒 Delegate 線程的 RunLoop 來讓其處理這些通知该镣。CFMultiplexerSource 會在 Delegate 線程的 RunLoop 對 Delegate 執(zhí)行實際的回調(diào)冻璃。

AFNetworking使用的常駐線程用于后臺接收delegate回調(diào),當(dāng)有任務(wù)需要處理時损合,通過performSelector:onThread:將任務(wù)提交給該線程的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中添加了的未接收任何消息的NSMachPort進(jìn)去,防止runloop退出進(jìn)而線程退出塌忽;

小知識

宏定義 do{ }while(0)

  • 幫助定義復(fù)雜的宏以避免錯誤
#define DO_SOMETHING() foo1();foo2();

if (a>0)
  DO_SOMETHING();
//展開后如下
if (a>0)
  foo1();
  foo2();
  • 避免使用goto跳轉(zhuǎn)

    int foo() {
      if (error1) {
          do_something();
          goto END:
      }
      if (error2) {
          do_something2();
          goto END;
      }
    END:
      xxx;
    }
    
    //使用do{}while(0)
    int foo() {
      do {
          if (error1) 
              do_something();
      if (error2) 
          do_something2();
      } while(0)
      
      xxx;
    }
    
  • 控制代碼塊

  • 避免由空宏定義造成的警告

    內(nèi)核中由于不同架構(gòu)的限制,很多時候會用到空宏失驶,土居。在編譯的時候,這些空宏會給出warning嬉探,為了避免這樣的warning擦耀,我們可以使用do{...}while(0)來定義空宏: #define EMPTYMICRO do{}while(0); 這種情況不太常見涩堤,因為有很多編譯器眷蜓,已經(jīng)支持空宏。

do{...}while(0)的妙用

CHECK_FOR_FORK()宏定義用途

主要對于非移動端平臺胎围,如Mac OSX吁系,進(jìn)程調(diào)用fork生成子進(jìn)程,一般是直接調(diào)用exec或類似的函數(shù)執(zhí)行新的程序白魂,而對于依賴Core Founadtion / Cocoa / Core Data 框架的應(yīng)用,必須調(diào)用 exec 函數(shù),否則這些框架也許不能正確的工作汽纤。

Warning: When launching separate processes using the fork function, you must always follow a call to fork with a call to exec or a similar function. Applications that depend on the Core Foundation, Cocoa, or Core Data frameworks (either explicitly or implicitly) must make a subsequent call to an exec function or those frameworks may behave improperly.

-- 摘自Threading Programming Guide

理解:應(yīng)該是避免進(jìn)程使用vfork系統(tǒng)調(diào)用繼續(xù)使用父進(jìn)程的數(shù)據(jù),導(dǎo)致影響父進(jìn)程福荸,因此要求立即調(diào)用exec去執(zhí)行新的程序蕴坪;

Reference

RunLoop --- CHECK_FOR_FORK()

RunLoop 源碼閱讀

Dispatch Sources

深入淺出 GCD 之 dispatch_queue

Threading Programming Guide -- Runloop

iOS刨根問底-深入理解RunLoop

CFRunLoopRef

深入理解RunLoop

iOS線下分享《RunLoop》by 孫源@sunnyxx

demo

https://github.com/FengyunSky/notes/blob/master/local/code/runloop.tar

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市敬锐,隨后出現(xiàn)的幾起案子背传,更是在濱河造成了極大的恐慌,老刑警劉巖台夺,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件径玖,死亡現(xiàn)場離奇詭異,居然都是意外死亡谒养,警方通過查閱死者的電腦和手機(jī)挺狰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門明郭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人丰泊,你說我怎么就攤上這事薯定。” “怎么了瞳购?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵话侄,是天一觀的道長。 經(jīng)常有香客問我学赛,道長年堆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任盏浇,我火速辦了婚禮变丧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绢掰。我一直安慰自己痒蓬,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布滴劲。 她就那樣靜靜地躺著攻晒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪班挖。 梳的紋絲不亂的頭發(fā)上鲁捏,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機(jī)與錄音萧芙,去河邊找鬼给梅。 笑死,一個胖子當(dāng)著我的面吹牛末购,可吹牛的內(nèi)容都是我干的破喻。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼盟榴,長吁一口氣:“原來是場噩夢啊……” “哼曹质!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起擎场,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤羽德,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后迅办,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宅静,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年站欺,在試婚紗的時候發(fā)現(xiàn)自己被綠了姨夹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纤垂。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖磷账,靈堂內(nèi)的尸體忽然破棺而出峭沦,到底是詐尸還是另有隱情,我是刑警寧澤逃糟,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布吼鱼,位于F島的核電站,受9級特大地震影響绰咽,放射性物質(zhì)發(fā)生泄漏菇肃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一取募、第九天 我趴在偏房一處隱蔽的房頂上張望琐谤。 院中可真熱鬧,春花似錦玩敏、人聲如沸笑跛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至几苍,卻和暖如春翻屈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背妻坝。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工伸眶, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人刽宪。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓厘贼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親圣拄。 傳聞我的和親對象是個殘疾皇子嘴秸,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,724評論 2 354

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

  • Runloop是iOS和OSX開發(fā)中非常基礎(chǔ)的一個概念庇谆,從概念開始學(xué)習(xí)岳掐。 RunLoop的概念 -般說,一個線程一...
    小貓仔閱讀 995評論 0 1
  • 轉(zhuǎn)自http://blog.ibireme.com/2015/05/18/runloop 深入理解RunLoop ...
    飄金閱讀 983評論 0 4
  • 原文地址:http://blog.ibireme.com/2015/05/18/runloop/ RunLoop ...
    大餅炒雞蛋閱讀 1,156評論 0 6
  • RunLoop 的概念 一般來講饭耳,一個線程一次只能執(zhí)行一個任務(wù)串述,執(zhí)行完成后線程就會退出。如果我們需要一個機(jī)制寞肖,讓線...
    Mirsiter_魏閱讀 618評論 0 2
  • 寶媽創(chuàng)業(yè)好項目纲酗,品牌童裝童品母嬰用品衰腌,一件代發(fā)! 招代理招加盟一手貨源一件代發(fā)觅赊,支持退換零囤貨右蕊,零風(fēng)險,售后服務(wù)質(zhì)...
    美麗心情88閱讀 118評論 0 0