RunLoop總結(jié)

RunLoop,從字面意思看叫做運(yùn)行循環(huán)另假。RunLoop基本作用:

  • 保持程序的持續(xù)運(yùn)行
  • 處理APP中的各種事件(觸摸事件像屋,定時(shí)器事件...)
  • 節(jié)省CPU資源,提高程序性能:有事做事边篮,沒事休息
    等等......

RunLoop內(nèi)部其實(shí)就是一個(gè)do-while循環(huán)己莺,在這個(gè)循環(huán)內(nèi)部不斷的處理各種事件,因此保證了程序不會(huì)退出一直運(yùn)行戈轿;

runloop示意圖

程序的main函數(shù)中凌受,UIApplicationMain函數(shù)內(nèi)部自動(dòng)啟動(dòng)了一個(gè)RunLoop,所以UIApplicationMain一直沒有返回思杯,保持程序持續(xù)運(yùn)行胜蛉。這個(gè)默認(rèn)啟動(dòng)的RunLoop是跟主線程相關(guān)聯(lián)的;

RunLoop對(duì)象:

iOS中有2套API訪問和使用RunLoop:

  • Foundation框架下的:NSRunLoop
  • CoreFoundation框架下的:CFRunLoopRef;
    CFRunLoopRef和NSRunLoop都代表RunLoop對(duì)象誊册;NSRunLoop是基于CFRunLoopRef的一層OC包裝奈梳,所以要了解RunLoop內(nèi)部結(jié)構(gòu),需要多研究CFRunLoopRef層的API解虱;

獲取RunLoop對(duì)象

// Foundation:
[NSRunLoop currentRunLoop];// 獲得當(dāng)前線程的RunLoop對(duì)象
[NSRunLoop mainRunLoop]; // 獲得主線程的RunLoop對(duì)象
//Core Foundation:
CFRunLoopGetCurrent();// 獲得當(dāng)前線程的RunLoop對(duì)象
CFRunLoopGetMain();// 獲得主線程的RunLoop對(duì)象

獲取RunLoop的源碼如下

// 存放Runloop的全局變量__CFRunLoops,key是pthread_t漆撞,value是Runloop
static CFMutableDictionaryRef __CFRunLoops = NULL;
static CFSpinLock_t loopsLock = CFSpinLockInit;

// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
    if (pthread_equal(t, kNilPthreadT)) { // t為0或?yàn)榭毡硎局骶€程
    t = pthread_main_thread_np();
    }
    __CFSpinLock(&loopsLock);
    //__CFRunLoops為空殴泰,則初始化__CFRunLoops,并創(chuàng)建主線程的Runloop浮驳,保存到__CFRunLoops中
    if (!__CFRunLoops) { 
        __CFSpinUnlock(&loopsLock);
    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
    CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
    CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
    if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
        CFRelease(dict);
    }
    CFRelease(mainLoop);
        __CFSpinLock(&loopsLock);
    }
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFSpinUnlock(&loopsLock);
    // 通過pthread_t獲取loop悍汛,如果沒獲取到,則新建一個(gè)loop保存到__CFRunLoops中至会,返回該loop
    if (!loop) {
    CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFSpinLock(&loopsLock);
    loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    if (!loop) {
        CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
        loop = newLoop;
    }
        // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
        __CFSpinUnlock(&loopsLock);
    CFRelease(newLoop);
    }
    if (pthread_equal(t, pthread_self())) {
        _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
        if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
            _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
        }
    }
    return loop;
}

可參考蘋果官方文檔:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html
蘋果開源代碼:
http://opensource.apple.com/source/CF/

CFRunLoopRef

源碼是這樣定義CFRunLoopRef的:

typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoop * CFRunLoopRef;

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; // runloop對(duì)應(yīng)的線程
    uint32_t _winthread;
    CFMutableSetRef _commonModes; //被標(biāo)記為commonMode的模式
    CFMutableSetRef _commonModeItems;//添加到commonMode的item
    CFRunLoopModeRef _currentMode; //Runloop當(dāng)前的運(yùn)行的模式
    CFMutableSetRef _modes; 
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFTypeRef _counterpart;
};

從代碼中可以看出Runloop主要包含線程pthread_t和模式CFRunLoopModeRef离咐。_commonModes存放已被標(biāo)記為“common”的mode。主線程的 RunLoop 里有兩個(gè)預(yù)置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode,這兩個(gè) Mode 都已經(jīng)被標(biāo)記為”Common”屬性奉件。_commonModeItem是哪些被添加到commonMode的source/timer/observer宵蛀。

RunLoop與線程的關(guān)系
  • 每條線程都有唯一一個(gè)與之對(duì)應(yīng)的RunLoop對(duì)象;
  • Runloop保存在一個(gè)全局的dictionary中县貌,線程為key术陶,Runloop為value;
  • 主線程的RunLoop程序啟動(dòng)后自動(dòng)創(chuàng)建好了煤痕,而子線程的RunLoop在第一次獲取時(shí)創(chuàng)建梧宫;
  • RunLoop在線程結(jié)束時(shí)銷毀;
RunLoop與RunLoopMode的關(guān)系
  • RunLoopMode代表RunLoop的運(yùn)行模式摆碉,每個(gè)RunLoop包含若干個(gè)Mode
  • 每次RunLoop啟動(dòng)時(shí)塘匣,只能指定其中一個(gè)Mode,這個(gè)Mode稱作CurrentMode巷帝,Runloop循環(huán)處理CurrentMode的所有事件忌卤;
  • 如果需要切換Mode,只能退出Loop锅睛,再重新指定一個(gè)Mode進(jìn)入埠巨;這樣可以分隔開不同mode的source、timer现拒、observer辣垒,互不影響;
  • 如果Mode里沒有任何source印蔬、timer勋桶、observer,Runloop立刻退出;
CFRunLoopModeRef(模式)

源碼中對(duì)CFRunLoopModeRef的定義:

typedef struct __CFRunLoopMode *CFRunLoopModeRef;

struct __CFRunLoopMode {
    CFStringRef _name;
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
    ....
};

從源碼中可以看到每個(gè)Mode包含若干個(gè)source0例驹、source1捐韩、timer、observer鹃锈;

系統(tǒng)默認(rèn)注冊(cè)了5個(gè)Mode:

  • KCFRunLoopDefaultMode:APP的默認(rèn)Mode荤胁,通常主線程在這個(gè)Mode下運(yùn)行;

  • UITrackingRunLoopMode:界面跟蹤Mode屎债,用于scrollView追蹤觸摸滑動(dòng)仅政,保證界面滑動(dòng)不受其他Mode影響;

  • UIInitializationRunLoopMode:在剛啟動(dòng)APP時(shí)進(jìn)入的第一個(gè)Mode盆驹,啟動(dòng)完成后就不再使用圆丹;

  • GSEventReceiveRunLoopMode:接受系統(tǒng)事件的內(nèi)部Mode,通常用不到躯喇;

  • KCFRunloopCommonModes:這是一個(gè)占位用的Mode辫封,不是真正的Mode;事件被標(biāo)記為KCFRunloopCommonMode后廉丽,Runloop運(yùn)行在UITrackingRunLoopMode和kCFRunLoopDefaultMode倦微,事件都能執(zhí)行;

CFRunLoopSourceRef(事件源)

Source0:觸摸事件正压、performSelector:onThread:
Source1:基于Port的線程間通信璃诀、系統(tǒng)事件捕捉,其回調(diào)函數(shù)為 __IOHIDEventSystemClientQueueCallback()蔑匣。

當(dāng)一個(gè)硬件事件(觸摸/鎖屏/搖晃等)發(fā)生后劣欢,首先由 IOKit.framework 生成一個(gè) IOHIDEvent 事件并由 SpringBoard 接收。SpringBoard 只接收按鍵(鎖屏/靜音等)裁良,觸摸凿将,加速,接近傳感器等幾種 Event价脾,隨后用 mach port 轉(zhuǎn)發(fā)給需要的App進(jìn)程牧抵。隨后蘋果注冊(cè)的那個(gè) Source1 就會(huì)觸發(fā)回調(diào),并調(diào)用 _UIApplicationHandleEventQueue() 進(jìn)行應(yīng)用內(nèi)部的分發(fā)侨把。

_UIApplicationHandleEventQueue() 會(huì)把 IOHIDEvent 處理并包裝成 UIEvent 進(jìn)行處理或分發(fā)犀变,其中包括識(shí)別 UIGesture/處理屏幕旋轉(zhuǎn)/發(fā)送給 UIWindow 等。通常事件比如 UIButton 點(diǎn)擊秋柄、touchesBegin/Move/End/Cancel 事件都是在這個(gè)回調(diào)中完成的获枝。

RunLoopTimer
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 */
};

RunLoopTimer是基于時(shí)間的觸發(fā)器,包含一個(gè)時(shí)間長度和回調(diào)骇笔。NSTimer注冊(cè)到Runloop后省店,Runloop會(huì)為重復(fù)的時(shí)間點(diǎn)注冊(cè)好事件嚣崭,時(shí)間間隔_interval,時(shí)間點(diǎn)到時(shí)Runloop會(huì)被喚醒執(zhí)行回調(diào)懦傍;Runloop為了節(jié)省資源雹舀,不會(huì)準(zhǔn)確按照時(shí)間點(diǎn)回調(diào)timer,_tolerance表示到了時(shí)間點(diǎn)后允許的最大誤差粗俱。如果某個(gè)時(shí)間點(diǎn)被錯(cuò)過了说榆,則這個(gè)時(shí)間點(diǎn)的回調(diào)會(huì)跳過,不會(huì)延后執(zhí)行寸认。

NSTimer的處理娱俺,會(huì)受到RunLoop的Mode影響。舉個(gè)例子:

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

上面這樣創(chuàng)建的處理的time只能在NSDefaultRunLoopMode下工作废麻,當(dāng)滾動(dòng)頁面時(shí),Runloop切換到UITrackingRunLoopMode模庐,timer失效烛愧;若想在NSDefaultRunLoopMode和UITrackingRunLoopMode模式下,timer都有效掂碱,應(yīng)將timer事件添加到KCFRunloopCommonModes怜姿;如下:

NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES];   
[[NSRunLoop currentRunLoop]addTimer:timer forMode: KCFRunloopCommonModes];
RunLoopObserver
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 */
};

RunLoopObserver是用來觀察Runloop的狀態(tài)的。當(dāng)Runloop狀態(tài)發(fā)生改變疼燥,觀察者能通過回調(diào)接收到這個(gè)變化沧卢。可檢測(cè)到Runloop以下狀態(tài):

/* 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
};
Runloop的運(yùn)行邏輯
void CFRunLoopRun(void) {   /* DOES CALLOUT */
    int32_t result;
    do {
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
        CHECK_FOR_FORK();
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}

從源碼可以看到醉者,Runloop做著do...while循環(huán)但狭,CFRunLoopRunSpecific函數(shù)返回值為kCFRunLoopRunStopped或kCFRunLoopRunFinished時(shí)退出循環(huán)。

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
        // 通知observer即將進(jìn)入Runloop
    if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    // Runloop循環(huán)處理事件撬即,__CFRunLoopRun函數(shù)只有在被打斷或沒有source/timer/observer時(shí)立磁,才會(huì)返回
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    //通知observer,Runloop即將退出
    if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
        __CFRunLoopModeUnlock(currentMode);
        __CFRunLoopPopPerRunData(rl, previousPerRun);
    rl->_currentMode = previousMode;
    return result;
}

進(jìn)入CFRunLoopRunSpecific函數(shù)剥槐,說明Runloop將要開始工作了唱歧,于是通知observer即將進(jìn)入Runloop。然后調(diào)用__CFRunLoopRun函數(shù)循環(huán)處理事件粒竖,函數(shù)返回颅崩,通知observer,Runloop即將退出蕊苗。直到Runloop被中斷或事件處理完畢沿后,整個(gè)Runloop循環(huán)結(jié)束;

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    Boolean didDispatchPortLastTime = true;
    int32_t retVal = 0;
    do {
          //   通知 Observers: RunLoop 即將觸發(fā) Timer 回調(diào)朽砰。
          if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
          // 通知 Observers: RunLoop 即將觸發(fā) Source0 (非port) 回調(diào)得运。
          if (rlm->_observerMask & kCFRunLoopBeforeSources)   __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
          // 執(zhí)行被加入的block
          __CFRunLoopDoBlocks(rl, rlm);
          //  RunLoop 觸發(fā) Source0 (非port) 回調(diào)膝蜈。
          Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);

          if (sourceHandledThisLoop) {
            // 執(zhí)行被加入的block
              __CFRunLoopDoBlocks(rl, rlm);
          }
          // 如果有 Source1 (基于port) 處于 ready 狀態(tài),直接處理這個(gè) Source1 然后跳轉(zhuǎn)去處理消息熔掺。
          if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0)) {
              goto handle_msg;
          }

          // 通知 Observers: RunLoop 的線程即將進(jìn)入休眠(sleep)
          __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
          __CFRunLoopSetSleeping(rl);
        /*
        調(diào)用 mach_msg 等待接受 mach_port 的消息饱搏。線程將進(jìn)入休眠, 直到被下面某一個(gè)事件喚醒
        ? 一個(gè)基于 port 的Source 的事件。
        ? 一個(gè) Timer 到時(shí)間了
        ? RunLoop 自身的超時(shí)時(shí)間到了
        ? 被其他什么調(diào)用者手動(dòng)喚醒
        */
          __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY);

          // 通知 Observers: RunLoop 的線程剛剛被喚醒了置逻。
          __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);

          // 收到消息推沸,處理消息。
          handle_msg:;
          __CFRunLoopSetIgnoreWakeUps(rl);

          //   Timer 到時(shí)間了券坞,觸發(fā)Timer的回調(diào)鬓催。
          if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
              CFRUNLOOP_WAKEUP_FOR_TIMER();
              __CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) 
          }
          //有dispatch回到main_queue的block,執(zhí)行block恨锚。
          else if (livePort == dispatchPort) {
              CFRUNLOOP_WAKEUP_FOR_DISPATCH();
              __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
          } else {
          //  Source1 (基于port) 發(fā)出事件宇驾,處理這個(gè)事件
              CFRUNLOOP_WAKEUP_FOR_SOURCE();
              // Despite the name, this works for windows handles as well
              __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
          }
      }
      // 執(zhí)行加入到Loop的block        
      __CFRunLoopDoBlocks(rl, rlm);
        
      if (sourceHandledThisLoop && stopAfterHandle) {
          // 進(jìn)入loop時(shí)參數(shù)說處理完事件就返回。
          retVal = kCFRunLoopRunHandledSource;
       } else if (timeout_context->termTSR < mach_absolute_time()) {
          // 超出傳入?yún)?shù)標(biāo)記的超時(shí)時(shí)間了
          retVal = kCFRunLoopRunTimedOut;
      } else if (__CFRunLoopIsStopped(rl)) {
          // 被外部調(diào)用者強(qiáng)制停止了
          __CFRunLoopUnsetStopped(rl);
          retVal = kCFRunLoopRunStopped;
      } else if (rlm->_stopped) {
          rlm->_stopped = false;
          retVal = kCFRunLoopRunStopped;
      } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
          // source/timer/observer一個(gè)都沒有了
          retVal = kCFRunLoopRunFinished;
      }
    } while (0 == retVal);

    return retVal;
}

__CFRunLoopRun函數(shù)是進(jìn)入Runloop后的處理邏輯猴伶。

RunLoop整個(gè)運(yùn)行過程如下:

  • 1.通知Observer课舍,將要進(jìn)入RunLoop

  • 2.通知Observer,將要處理timer

  • 3.通知Observer他挎,即將觸發(fā)非基于端口的輸入源(source0事件)筝尾。

  • 4.處理block

  • 5.觸發(fā)的source0事件(可能會(huì)再次處理)

  • 6.如果存在source1,調(diào)轉(zhuǎn)到第8步

  • 7.通知Observer開始休眠

  • 8.通知Observer結(jié)束休眠
    1办桨、處理timer
    2筹淫、處理GCD Async To Main Queue
    3、處理source1

  • 9.處理Blocks

  • 10.根據(jù)前面執(zhí)行的結(jié)果呢撞,決定如何操作
    1损姜、回到第2步
    2、退出RunLoop

  • 11.通知Observer退出RunLoop

  • 9.處理待處理事件殊霞。
    如果觸發(fā)了用戶定義的計(jì)時(shí)器薛匪,則處理計(jì)時(shí)器事件并重新啟動(dòng)循環(huán)。轉(zhuǎn)到第2步脓鹃。
    如果輸入源被觸發(fā)逸尖,則傳遞事件。
    如果運(yùn)行循環(huán)被明確喚醒但尚未超時(shí)瘸右,請(qǐng)重新啟動(dòng)循環(huán)娇跟。轉(zhuǎn)到第2步。

  • 10.通知觀察者運(yùn)行循環(huán)已退出太颤。

參考博客:
深入理解RunLoop

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末苞俘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子龄章,更是在濱河造成了極大的恐慌吃谣,老刑警劉巖乞封,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異岗憋,居然都是意外死亡肃晚,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門仔戈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來关串,“玉大人,你說我怎么就攤上這事监徘〗蓿” “怎么了?”我有些...
    開封第一講書人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵凰盔,是天一觀的道長墓卦。 經(jīng)常有香客問我,道長户敬,這世上最難降的妖魔是什么落剪? 我笑而不...
    開封第一講書人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮山叮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘添履。我一直安慰自己屁倔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開白布暮胧。 她就那樣靜靜地躺著锐借,像睡著了一般。 火紅的嫁衣襯著肌膚如雪往衷。 梳的紋絲不亂的頭發(fā)上钞翔,一...
    開封第一講書人閱讀 51,775評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音席舍,去河邊找鬼布轿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛来颤,可吹牛的內(nèi)容都是我干的汰扭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼福铅,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼萝毛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起滑黔,我...
    開封第一講書人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤笆包,失蹤者是張志新(化名)和其女友劉穎环揽,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體庵佣,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡歉胶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秧了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跨扮。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖验毡,靈堂內(nèi)的尸體忽然破棺而出衡创,到底是詐尸還是另有隱情,我是刑警寧澤晶通,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布璃氢,位于F島的核電站,受9級(jí)特大地震影響狮辽,放射性物質(zhì)發(fā)生泄漏一也。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一喉脖、第九天 我趴在偏房一處隱蔽的房頂上張望椰苟。 院中可真熱鬧,春花似錦树叽、人聲如沸舆蝴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽洁仗。三九已至,卻和暖如春性锭,著一層夾襖步出監(jiān)牢的瞬間赠潦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來泰國打工草冈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留她奥,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓怎棱,卻偏偏與公主長得像方淤,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蹄殃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

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

  • 前言 最近離職了,可以盡情熬夜寫點(diǎn)總結(jié)携茂,不用擔(dān)心第二天上班爽并蛋疼著,這篇的主角 RunLoop 一座大山诅岩,涵蓋的...
    zerocc2014閱讀 12,376評(píng)論 13 67
  • 參考資料:ibireme :http://blog.ibireme.com/2015/05/18/runloop/...
    LiYaoPeng閱讀 12,928評(píng)論 2 19
  • RunLoop簡(jiǎn)介 RunLoop 是一個(gè)運(yùn)行循環(huán),內(nèi)部類似do-while循環(huán)讳苦,線程進(jìn)入并使用它來運(yùn)行響應(yīng)輸入事...
    伶俐ll閱讀 386評(píng)論 0 4
  • 發(fā)現(xiàn)自己對(duì)于找女盆友的真實(shí)想法带膜,,其實(shí)一直對(duì)找女朋友沒興趣的鸳谜,只是因?yàn)檫@段時(shí)間的空白讓我覺得遺憾膝藕,然后才稍微的想找...
    我有個(gè)帥哥夢(mèng)閱讀 171評(píng)論 0 0
  • 漫天的迷霧遮住了我的雙眼, 眺望你要離去的身影咐扭,堅(jiān)決芭挽,狠心。 你的離去蝗肪,我的眼里有些許的迷離袜爪,差一點(diǎn)點(diǎn)潸然淚下,我...
    心是甜甜的閱讀 355評(píng)論 0 1