Runloop 理解總結(jié)

什么是Runloop

  • 為了實(shí)現(xiàn)線程能在有事件喚起的時(shí)候?qū)崟r(shí)處理Event,并且在沒(méi)有事件的時(shí)候進(jìn)入休眠并不退出贮聂,繼續(xù)等待下一次事件消息的喚醒的機(jī)制帐我,所以出現(xiàn)了和很多系統(tǒng)中Eventloop機(jī)制類(lèi)似的Runloop機(jī)制。

Runloop的實(shí)質(zhì)

  • 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ù)返回描沟。

Runloops 的功能

  • 使程序一直運(yùn)行并接受用戶(hù)輸入
  • 決定程序在何時(shí)應(yīng)該處理哪些Event
  • 調(diào)用解耦 (SmallTalk/Message Queue)
  • 節(jié)省CPU時(shí)間

Runloops的Cocoa實(shí)現(xiàn)

  • Foundation 中實(shí)現(xiàn)了 NSRunloop 框架
  • NSRunloop 基于 CFRunloop封裝而來(lái)飒泻,CFRunloop基于c/c++封裝,代碼開(kāi)源
  • 支持CFRunloop運(yùn)行的核心內(nèi)容有 GCD, XNU的mach內(nèi)核吏廉,block回調(diào)機(jī)制泞遗,pthread等
  • 依賴(lài)Runloop運(yùn)行的框架、類(lèi)席覆、方法:NSTimer, UIEvent, Autorelease Pool, performThread和 performDelay等史辙,CADisplayLink,CATransition佩伤,CAAnimation,dispatch_get_main_queue(),NSURLConnection,AFNetworking,

Runloop 的 類(lèi)結(jié)構(gòu)分析

1.CFRunLoopSourceRef

  • Source 是 RunLoop的數(shù)據(jù)抽象類(lèi) (protocol)
  • Runloop定義了兩個(gè)Version的Source(事件產(chǎn)生的地方):
    1. Source0: 基于非Port的事件,只包含了一個(gè)回調(diào)(函數(shù)指針)聊倔,它并不能主動(dòng)觸發(fā)事件。一般是App自己負(fù)責(zé)管理及觸發(fā)如UIEvent/CFSocket生巡。使用時(shí)耙蔑,你需要先調(diào)用 CFRunLoopSourceSignal(source),將這個(gè) Source 標(biāo)記為待處理孤荣,然后手動(dòng)調(diào)用 CFRunLoopWakeUp(runloop) 來(lái)喚醒 RunLoop甸陌,讓其處理這個(gè)事件。
    2. Source1: 包含了一個(gè) mach_port 和一個(gè)回調(diào)(函數(shù)指針)垃环,被用于通過(guò)內(nèi)核和其他線程相互發(fā)送消息.由RunLoop和內(nèi)核管理邀层,Mach port驅(qū)動(dòng),如CFMachPort遂庄、CFMessagePort

2.CFRunloopTimerRef

基于時(shí)間的觸發(fā)器寥院,它和 NSTimer 是toll-free bridged 的,可以混用涛目。其包含一個(gè)時(shí)間長(zhǎng)度和一個(gè)回調(diào)(函數(shù)指針)秸谢。當(dāng)其加入到 RunLoop 時(shí)凛澎,RunLoop會(huì)注冊(cè)對(duì)應(yīng)的時(shí)間點(diǎn),當(dāng)時(shí)間點(diǎn)到時(shí)估蹄,RunLoop會(huì)被喚醒以執(zhí)行那個(gè)回調(diào)塑煎。

3.CFRunloopObserverRef

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
};

RUNLoopObserver 與 AutoreleasePool

  • AutoreleasePool
    App啟動(dòng)后,蘋(píng)果在主線程 RunLoop 里注冊(cè)了兩個(gè) Observer臭蚁,其回調(diào)都是 _wrapRunLoopWithAutoreleasePoolHandler()最铁。

  • 第一個(gè) Observer 監(jiān)視的事件是 Entry(即將進(jìn)入Loop),其回調(diào)內(nèi)會(huì)調(diào)用 _objc_autoreleasePoolPush() 創(chuàng)建自動(dòng)釋放池垮兑。其 order 是-2147483647冷尉,優(yōu)先級(jí)最高,保證創(chuàng)建釋放池發(fā)生在其他所有回調(diào)之前系枪。

  • 第二個(gè) Observer 監(jiān)視了兩個(gè)事件: BeforeWaiting(準(zhǔn)備進(jìn)入休眠) 時(shí)調(diào)用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池并創(chuàng)建新池雀哨;Exit(即將退出Loop) 時(shí)調(diào)用 _objc_autoreleasePoolPop() 來(lái)釋放自動(dòng)釋放池。這個(gè) Observer 的 order 是 2147483647私爷,優(yōu)先級(jí)最低雾棺,保證其釋放池子發(fā)生在其他所有回調(diào)之后。

RunloopMode

  1. Runloop 在同一段時(shí)間只能且必須在一種特定的mode下Run
  2. 更換Mode時(shí)衬浑,必須Stop當(dāng)前Loop捌浩,重齊新Loop
1.NSDefaulyRunLoopMode

默認(rèn)狀態(tài)、空閑狀態(tài)

2.UITrackingRunLoopMode

滑動(dòng)態(tài)mode -追蹤 ScrollView 滑動(dòng)時(shí)的狀態(tài)

3.UIInitializationRunLoopMode

私有嚎卫,啟動(dòng)APP時(shí)切換進(jìn)入嘉栓,完成啟動(dòng)就被拋棄不再使用

4.GSEventReceiveRunLoopMode

接受系統(tǒng)事件的內(nèi)部Mode,通常用不到拓诸。

5.NSRunLoopCommonModes

這個(gè)mode比較特殊侵佃,包含了DefaultMode和UITrackingRunLoopMode 的狀態(tài),一般作占位用奠支,結(jié)構(gòu)也比較特殊

應(yīng)用: UITrakingRunloopMode 與 Timer

主線程的 RunLoop 里有兩個(gè)預(yù)置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode馋辈。這兩個(gè) Mode 都已經(jīng)被標(biāo)記為”Common”屬性。DefaultMode 是 App 平時(shí)所處的狀態(tài)倍谜,TrackingRunLoopMode 是追蹤 ScrollView 滑動(dòng)時(shí)的狀態(tài)迈螟。當(dāng)你創(chuàng)建一個(gè) Timer 并加到 DefaultMode 時(shí),Timer 會(huì)得到重復(fù)回調(diào)尔崔,但此時(shí)滑動(dòng)一個(gè)TableView時(shí)答毫,RunLoop 會(huì)將 mode 切換為 TrackingRunLoopMode,這時(shí) Timer 就不會(huì)被回調(diào)季春,并且也不會(huì)影響到滑動(dòng)操作洗搂。

// 默認(rèn)添加到DefaultMode中
NSTimer *timer= [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];
// 添加到CommonModes中去
NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:1] interval:1 target:testObject4 selector:@selector(timerAction:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
RunLoopMode的切換
  1. Run RunLoop對(duì)象需要每次調(diào)用int CFRunLoopRunSpecific(runloop, modeName, seconds, stopAfterHandle)需要指定一個(gè)ModeName進(jìn)入
  2. 當(dāng)有滑動(dòng)事件響應(yīng)后,主線程的RunLoop對(duì)象會(huì)喚醒RunLoop對(duì)象,調(diào)用主函數(shù)傳入TrackingMode的Name切換到滑動(dòng)Mode中處理相應(yīng)的Source

Runloop 運(yùn)行流程

/// 用DefaultMode啟動(dòng)
void CFRunLoopRun(void) {
    CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
}
 
/// 用指定的Mode啟動(dòng)耘拇,允許設(shè)置RunLoop超時(shí)時(shí)間
int CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean stopAfterHandle) {
    return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}
 
/// RunLoop的實(shí)現(xiàn)
int CFRunLoopRunSpecific(runloop, modeName, seconds, stopAfterHandle) {
    
    /// 首先根據(jù)modeName找到對(duì)應(yīng)mode
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(runloop, modeName, false);
    /// 如果mode里沒(méi)有source/timer/observer, 直接返回撵颊。
    if (__CFRunLoopModeIsEmpty(currentMode)) return;
    
    /// 1. 通知 Observers: RunLoop 即將進(jìn)入 loop。
    __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry);
    
    /// 內(nèi)部函數(shù)惫叛,進(jìn)入loop
    __CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) {
        
        Boolean sourceHandledThisLoop = NO;
        int retVal = 0;
        do {
 
            /// 2. 通知 Observers: RunLoop 即將觸發(fā) Timer 回調(diào)倡勇。
            __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers);
            /// 3. 通知 Observers: RunLoop 即將觸發(fā) Source0 (非port) 回調(diào)。
            __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources);
            /// 執(zhí)行被加入的block
            __CFRunLoopDoBlocks(runloop, currentMode);
            
            /// 4. RunLoop 觸發(fā) Source0 (非port) 回調(diào)嘉涌。
            sourceHandledThisLoop = __CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle);
            /// 執(zhí)行被加入的block
            __CFRunLoopDoBlocks(runloop, currentMode);
 
            /// 5. 如果有 Source1 (基于port) 處于 ready 狀態(tài)妻熊,直接處理這個(gè) Source1 然后跳轉(zhuǎn)去處理消息。
            if (__Source0DidDispatchPortLastTime) {
                Boolean hasMsg = __CFRunLoopServiceMachPort(dispatchPort, &msg)
                if (hasMsg) goto handle_msg;
            }
            
            /// 通知 Observers: RunLoop 的線程即將進(jìn)入休眠(sleep)洛心。
            if (!sourceHandledThisLoop) {
                __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting);
            }
            
            /// 7. 調(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) {
                mach_msg(msg, MACH_RCV_MSG, port); // thread wait for receive msg
            }
 
            /// 8. 通知 Observers: RunLoop 的線程剛剛被喚醒了。
            __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting);
            
            /// 收到消息番枚,處理消息法严。
            handle_msg:
 
            /// 9.1 如果一個(gè) Timer 到時(shí)間了,觸發(fā)這個(gè)Timer的回調(diào)葫笼。
            if (msg_is_timer) {
                __CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time())
            } 
 
            /// 9.2 如果有dispatch到main_queue的block深啤,執(zhí)行block。
            else if (msg_is_dispatch) {
                __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            } 
 
            /// 9.3 如果一個(gè) Source1 (基于port) 發(fā)出事件了路星,處理這個(gè)事件
            else {
                CFRunLoopSourceRef source1 = __CFRunLoopModeFindSourceForMachPort(runloop, currentMode, livePort);
                sourceHandledThisLoop = __CFRunLoopDoSource1(runloop, currentMode, source1, msg);
                if (sourceHandledThisLoop) {
                    mach_msg(reply, MACH_SEND_MSG, reply);
                }
            }
            
            /// 執(zhí)行加入到Loop的block
            __CFRunLoopDoBlocks(runloop, currentMode);
            
 
            if (sourceHandledThisLoop && stopAfterHandle) {
                /// 進(jìn)入loop時(shí)參數(shù)說(shuō)處理完事件就返回溯街。
                retVal = kCFRunLoopRunHandledSource;
            } else if (timeout) {
                /// 超出傳入?yún)?shù)標(biāo)記的超時(shí)時(shí)間了
                retVal = kCFRunLoopRunTimedOut;
            } else if (__CFRunLoopIsStopped(runloop)) {
                /// 被外部調(diào)用者強(qiáng)制停止了
                retVal = kCFRunLoopRunStopped;
            } else if (__CFRunLoopModeIsEmpty(runloop, currentMode)) {
                /// source/timer/observer一個(gè)都沒(méi)有了
                retVal = kCFRunLoopRunFinished;
            }
            
            /// 如果沒(méi)超時(shí),mode里沒(méi)空洋丐,loop也沒(méi)被停止呈昔,那繼續(xù)loop。
        } while (retVal == 0);
    }
    
    /// 10. 通知 Observers: RunLoop 即將退出友绝。
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
  1. 掛起
/// 7. 調(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) {
     mach_msg(msg, MACH_RCV_MSG, port); // thread wait for receive msg
}

上面的函數(shù)主要實(shí)現(xiàn)了兩個(gè)步驟

1.指定用于喚醒port的端口
2.調(diào)用mach_msg監(jiān)聽(tīng)喚醒端口郭宝,被喚醒前,將這個(gè)線程掛起掷漱,停留在mach_msg_trap狀態(tài)
  1. 喚醒
mach_msg(msg,MACH_RCV_MSG,port)

向指定端口發(fā)送port消息粘室,喚醒Runloop

應(yīng)用

tableViewCell 優(yōu)化
// 當(dāng)滑動(dòng)狀態(tài)下就不進(jìn)行設(shè)置圖片操作
UIImage *download = ...;
[self.avatarImageView perfomSelector:@selector(setImage:) withObject:download afterDelay:0 inModes:@[NSDefaultRunloopMode]];
signal救回
CFRunloopRef runloop = CFRunloopGetCurrent();
NSArray *allModes = CFBridgingRelease(XFRunLoopCopyAllModes(runloop));
while(1){
    for (NString *mode in allModes) {
        CFRunloopRunInMode((CFStringRef)mode, 0.001, false);
    }
}

可以做最后的收集挽回工作(彈窗提示等)
此處后面在崩潰信息處補(bǔ)充

一次觸摸事件的回應(yīng)

具體流程參照應(yīng)用 -- 圖形響應(yīng)鏈
1.蘋(píng)果注冊(cè)了一個(gè) Source1 (基于 mach port 的) 用來(lái)接收系統(tǒng)事件,其回調(diào)函數(shù)為 __IOHIDEventSystemClientQueueCallback()

  1. 當(dāng)我們觸發(fā)了事件(觸摸/鎖屏/搖晃等)后卜范,由IOKit.framework生成一個(gè) IOHIDEvent事件

    (IOKit是蘋(píng)果的硬件驅(qū)動(dòng)框架衔统,它專(zhuān)門(mén)處理用戶(hù)交互設(shè)備,由IOHIDServices和IOHIDDisplays兩部分組成,其中IOHIDServices是專(zhuān)門(mén)處理用戶(hù)交互的缰冤,它會(huì)將事件封裝成IOHIDEvents對(duì)象)

  2. 然后這些事件又由SpringBoard接收犬缨,它只接收收按鍵(鎖屏/靜音等),觸摸棉浸,加速怀薛,接近傳感器等幾種 Event
  3. 接著用mach_port轉(zhuǎn)發(fā)給需要的App進(jìn)程.隨后蘋(píng)果注冊(cè)的那個(gè)Source1 就會(huì)接收IOHIDEvent,之后再回調(diào)__IOHIDEventSystemClientQueueCallback()迷郑,觸發(fā)一個(gè)Source0資源枝恋,Source觸發(fā)了回調(diào) _UIApplicationHandleEventQueue()進(jìn)行應(yīng)用內(nèi)部的分發(fā)。_UIApplicationHandleEventQueue()把IOHIDEvent處理包裝成UIEvent進(jìn)行處理分發(fā)嗡害,我們平時(shí)的UIGesture/處理屏幕旋轉(zhuǎn)/發(fā)送給 UIWindow/UIButton 點(diǎn)擊焚碌、touchesBegin/Move/End/Cancel這些事件,都是在這個(gè)回調(diào)中完成
  4. 當(dāng)上面的 _UIApplicationHandleEventQueue() 識(shí)別了一個(gè)手勢(shì)時(shí)霸妹,其首先會(huì)調(diào)用 Cancel 將當(dāng)前的 touchesBegin/Move/End 系列回調(diào)打斷十电。隨后系統(tǒng)將對(duì)應(yīng)的 UIGestureRecognizer 標(biāo)記為待處理。
  5. 蘋(píng)果注冊(cè)了一個(gè) Observer 監(jiān)測(cè) BeforeWaiting (Loop即將進(jìn)入休眠) 事件叹螟,這個(gè)Observer的回調(diào)函數(shù)是 _UIGestureRecognizerUpdateObserver()鹃骂,其內(nèi)部會(huì)獲取所有剛被標(biāo)記為待處理的 GestureRecognizer,并執(zhí)行GestureRecognizer的回調(diào)罢绽。
  6. 當(dāng)有 UIGestureRecognizer 的變化(創(chuàng)建/銷(xiāo)毀/狀態(tài)改變)時(shí)畏线,這個(gè)回調(diào)都會(huì)進(jìn)行相應(yīng)處理。

該文的參考資料

1.深入理解RunLoop https://blog.ibireme.com/2015/05/18/runloop/
2. Runloop 線下分享 http://v-wb.youku.com/v_show/id_XODgxODkzODI0.html#paction
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末良价,一起剝皮案震驚了整個(gè)濱河市寝殴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌明垢,老刑警劉巖蚣常,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異袖外,居然都是意外死亡史隆,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén)曼验,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)泌射,“玉大人,你說(shuō)我怎么就攤上這事鬓照∪劭幔” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵豺裆,是天一觀的道長(zhǎng)拒秘。 經(jīng)常有香客問(wèn)我号显,道長(zhǎng),這世上最難降的妖魔是什么躺酒? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任押蚤,我火速辦了婚禮,結(jié)果婚禮上羹应,老公的妹妹穿的比我還像新娘揽碘。我一直安慰自己,他們只是感情好园匹,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布雳刺。 她就那樣靜靜地躺著,像睡著了一般裸违。 火紅的嫁衣襯著肌膚如雪掖桦。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天供汛,我揣著相機(jī)與錄音枪汪,去河邊找鬼。 笑死紊馏,一個(gè)胖子當(dāng)著我的面吹牛料饥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播朱监,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼原叮!你這毒婦竟也來(lái)了赫编?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤奋隶,失蹤者是張志新(化名)和其女友劉穎擂送,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體唯欣,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嘹吨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了境氢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蟀拷。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖萍聊,靈堂內(nèi)的尸體忽然破棺而出问芬,到底是詐尸還是另有隱情,我是刑警寧澤寿桨,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布此衅,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏挡鞍。R本人自食惡果不足惜骑歹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望墨微。 院中可真熱鬧道媚,春花似錦、人聲如沸欢嘿。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)炼蹦。三九已至羡宙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間掐隐,已是汗流浹背狗热。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留虑省,地道東北人匿刮。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像探颈,于是被迫代替她去往敵國(guó)和親熟丸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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