OC底層原理探索-NSRunLoop

RunLoop應(yīng)用

image.png

這張圖是蘋果官網(wǎng)中圖烹俗,接下來通過示例理解這種圖


- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    // 循環(huán)引用
    
    [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
        // __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
        NSLog(@"1111");
    }];
//
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(gotNotification:) name:@"MyNotification" object:nil];
       
       [self performSelector:@selector(fire) withObject:nil afterDelay:1.0];
////
       dispatch_async(dispatch_get_main_queue(), ^{
           //__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
           NSLog(@"hello word");
       });
//
       void (^block)(void) = ^{
           //__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__
           NSLog(@"123");
       };

       block();
}

- (void)fire {
    //__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
    NSLog(@"performSeletor");
}

- (void)gotNotification:(NSNotification *)noti {
    
     NSLog(@"gotNotification = %@",noti);
}

#pragma mark - 觸摸事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSLog(@"來了,老弟!!!");
    // __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
    [[NSNotificationCenter defaultCenter] postNotificationName:@"MyNotification" object:@"ssl"];
}

@end

首先測試下NStimer躏吊,斷點bt下

image.png

  • 這里timer收到runloop影響
  • 這里有個__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__

測試下block

image.png

  • 這里block收到runloop影響
  • 這里有個__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__

這里就不一一進行測試了,給出其余幾種情況

  • timer:__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
  • GCD主隊列:__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
  • source0:__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
  • source1:__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
  • observer :__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
  • block:__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__

RunLoop作用

  • 保持程序的持續(xù)運行
  • 處理App的各種時間(觸摸、定時器等)
  • 節(jié)省CPU資源、提供程序的性能:做事的時候做事、休息的時候休息

RunLoop與線程關(guān)系

CFRunLoop源碼中找到CFRunLoopGetMainCFRunLoopGetCurrent方法

image.png

進入_CFRunLoopGet0方法


CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
    if (pthread_equal(t, kNilPthreadT)) {
        t = pthread_main_thread_np();
    }
    __CFLock(&loopsLock);
    if (!__CFRunLoops) {
        __CFUnlock(&loopsLock);

    // 創(chuàng)建一個可變字典
    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
        
    // 創(chuàng)建主線程runloop
    CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
        
    // 進行綁定 dict[@"pthread_main_thread_np"] = mainLoop
    CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
        
        
    if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
        CFRelease(dict);
    }
    CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
    // 根據(jù)當(dāng)前線程獲取loop
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFUnlock(&loopsLock);
    // 如果不是主線程腋舌,進行類似操作將線程和創(chuàng)建的CFRunLoopRef進行綁定
    if (!loop) {
    CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFLock(&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
        __CFUnlock(&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;
}

  • 如果__CFRunLoops不存在,創(chuàng)建CFMutableDistionaryRef,并默認初始化主線程的RunLoop并將RunLoop放入到字典中,線程為key渗蟹,runloop為value
  • 在通過線程獲取RunLoop時块饺,以key-value方式從字典中獲取對應(yīng)的RunLoop;
  • 如果RunLoop為空,則創(chuàng)建一個newLoop雌芽,以線程為key授艰,RunLoop為value,存儲到__CFRunLoops中
【線程總結(jié)】線程的RunLoop會默認被創(chuàng)建世落,而子線程的RunLoop是懶加載的淮腾,需要時才會創(chuàng)建,RunLoop和線程是一對一的關(guān)系,存儲在一個字典中谷朝。
  • 子線程runLoop分析
    dispatch_queue_t queue = dispatch_queue_create("", NULL);
    dispatch_async(queue, ^{
        NSLog(@"running....");

          [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
              NSLog(@"helloc timer...%@", [NSThread currentThread]);
          }];
        
    });

image.png

由上圖可知洲押,這里只打印了定時器前的打印,定時任務(wù)并沒有執(zhí)行圆凰,這是因為NSTimer需要依賴于RunLoop杈帐,主線程的RunLoop默認開啟,而子線程的RunLoop是懶加載专钉,需要手動開啟挑童。
image.png

RunLoop數(shù)據(jù)結(jié)構(gòu)

創(chuàng)建RunLoop是使用的__CFRunLoopCreate函數(shù),查看函數(shù)實現(xiàn):

image.png

CFRunLoopRef是結(jié)構(gòu)體指針跃须,查看__CFRunLoop結(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;
    uint32_t _winthread;
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFAbsoluteTime _runTime;
    CFAbsoluteTime _sleepTime;
    CFTypeRef _counterpart;
};

由這個結(jié)構(gòu)體站叼,可以得知一個runLoop對應(yīng)_commonModes,這個_commonModes是個集合類型,所以一個runLoop對應(yīng)多個mode

mode定義

struct __CFRunLoopMode {
    CFStringRef _name;
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
    ...
};
  • CFRunLoopModeRef代表RunLoop的運行模式菇民;
  • 一個RunLoop包含若干個 Mode尽楔,每個 Mode 又包含若干個Source0/Source1/Timer/Observer;
  • RunLoop啟動時只能選擇其中一個 Mode第练,作為 currentMode翔试;
    如果需要切換 Mode,只能退出當(dāng)前 Loop复旬,再重新選擇一個 Mode 進入,切換模式不會導(dǎo)致程序退出冲泥;
  • 不同 Mode 中的Source0/Source1/Timer/Observer能分隔開來驹碍,互不影響;
  • 如果 Mode 里沒有任何Source0/Source1/Timer/Observer凡恍,RunLoop會立馬退出志秃。

__CFRunLoopMode源碼定義中包括了4個set集合_sources0、_sources1嚼酝、_observers浮还、_timers,這四個集合也就是我們常說的事件(事務(wù))闽巩。所以我們可以得出結(jié)論:CFRunLoopMode和sourses钧舌、timer、observer也是一對多的關(guān)系涎跨。

這里的timer洼冻、observer比較好理解,什么是_sources0隅很、_sources1呢撞牢?

image.png

mode類型
-kCFRunLoopDefaultMode 默認的運行模式,通常主線程是在這個Mode下運行
-UITrackingRunLoopMode 界面跟蹤Mode,用于ScrollView等視圖屋彪,追蹤觸摸滑動所宰,保證界面的滑動不受其他Mode的影響
-UIInitializationRunLoopMode 在剛啟動App時進入的第一個Mode,啟動完成后就不在使用
-GSEventReceiveRunLoopMode 接受系統(tǒng)時間的內(nèi)部Mode畜挥,通常用不到
-kCFRunLoopCommonModes 是一個偽模式仔粥,可以在標記為CommonModes的模式下運行,RunLoop會自動將_commonModeItems里的source砰嘁、observe件炉、timer同步到具有標記的Mode里。

image.png

runLoop原理

在源碼中查找CFRunLoopRunSpecific的方法實現(xiàn)矮湘,見下圖:

image.png

由上圖可知:這個函數(shù)是對runLoop的生命周期的處理,進入__CFRunLoopRun

/**
 *  運行 run loop
 *
 *  @param rl              運行的RunLoop對象
 *  @param rlm             運行的mode
 *  @param seconds         run loop超時時間
 *  @param stopAfterHandle true:run loop處理完事件就退出  false:一直運行直到超時或者被手動終止
 *  @param previousMode    上一次運行的mode
 *
 *  @return 返回4種狀態(tài)
 */
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    
    int32_t retVal = 0;
    
    do {  // itmes do
        
        /// 2. 通知 Observers: RunLoop 即將觸發(fā) Timer 回調(diào)斟冕。
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        
        /// 3. 通知 Observers: RunLoop 即將觸發(fā) Source0 (非port) 回調(diào)。
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources)
        
        /// 執(zhí)行被加入的block
        __CFRunLoopDoBlocks(rl, rlm);
        
        /// 4. RunLoop 觸發(fā) Source0 (非port) 回調(diào)缅阳。
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        
        /// 處理sources0返回為YES
        if (sourceHandledThisLoop) {
            /// 執(zhí)行被加入的block
            __CFRunLoopDoBlocks(rl, rlm);
        }
        
        /// 5. 如果有 Source1 (基于port) 處于 ready 狀態(tài)磕蛇,直接處理這個 Source1 然后跳轉(zhuǎn)去處理消息。
        if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
            /// 如果接收到了消息的話十办,前往第9步開始處理消息
            goto handle_msg;
        }
        
        /// 6.通知 Observers: RunLoop 的線程即將進入休眠(sleep)秀撇。
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        __CFRunLoopSetSleeping(rl);
        
        /// 7. 接收waitSet端口的消息
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
        
        /// 7. 等待接受 mach_port 的消息。線程將進入休眠, 直到被下面某一個事件喚醒向族。
        /// ? 一個基于 port 的Source 的事件呵燕。
        /// ? 一個 Timer 到時間了
        /// ? RunLoop 自身的超時時間到了
        /// ? 被其他什么調(diào)用者手動喚醒
        
        // 取消runloop的休眠狀態(tài)
        __CFRunLoopUnsetSleeping(rl);
        
        /// 8. 通知 Observers: RunLoop 的線程剛剛被喚醒了。
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
        
    handle_msg:
        if (被Timer喚醒) {
            /// 9.1 如果一個 Timer 到時間了件相,觸發(fā)這個Timer的回調(diào)再扭。
            __CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
        } else if (被GCD喚醒) {
            /// 9.2 如果有dispatch到main_queue的block夜矗,執(zhí)行block
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
        } else if (被Source1喚醒) {
            /// 9.3 如果一個 Source1 (基于port) 發(fā)出事件了泛范,處理這個事件
            __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
        }
        
        /// 執(zhí)行加入到Loop的block
        __CFRunLoopDoBlocks(rl, rlm);
        
        if (sourceHandledThisLoop && stopAfterHandle) {
            /// 進入loop時參數(shù)說處理完事件就返回。
            retVal = kCFRunLoopRunHandledSource;
        } else if (timeout_context->termTSR < mach_absolute_time()) {
            /// 超出傳入?yún)?shù)標記的超時時間了
            retVal = kCFRunLoopRunTimedOut;
        } else if (__CFRunLoopIsStopped(rl)) {
            /// 被外部調(diào)用者強制停止了
            __CFRunLoopUnsetStopped(rl);
            retVal = kCFRunLoopRunStopped;
        } else if (rlm->_stopped) {
            /// 自動停止了
            rlm->_stopped = false;
            retVal = kCFRunLoopRunStopped;
        } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
            /// source/timer/observer一個都沒有了
            retVal = kCFRunLoopRunFinished;
        }
        /// 如果沒超時紊撕,mode里沒空罢荡,loop也沒被停止,那繼續(xù)loop对扶。
    } while (0 == retVal);
    
    return retVal;
}

下圖就是上圖的處理邏輯

image.png

【總結(jié)】
RunLoop是通過系統(tǒng)內(nèi)部維護的循環(huán)進行事件区赵、消息管理的一個對象。RunLoop實際上就是一個do...while循環(huán)浪南,有任務(wù)時開始惧笛,無任務(wù)時休眠。本質(zhì)是通過mach_msg()函數(shù)接收逞泄、發(fā)送消息患整。

CFRunLoopModeRef 這樣設(shè)計有什么好處拜效?Runloop為什么會有多個 Mode?

-Mode 做到了屏蔽的效果各谚,當(dāng)RunLoop運行在 Mode1 下面的時候紧憾,是處理不了 Mode2 的事件的;
比如NSDefaultRunLoopMode默認模式和UITrackingRunLoopMode滾動模式昌渤,滾動屏幕的時候就會切換到滾動模式赴穗,就不用去處理默認模式下的事件了,保證了 UITableView等的滾動順暢膀息。

RunLoop與線程的關(guān)系:

  • 每個線程都有一個與之對應(yīng)的RunLoop般眉,所以RunLoop與線程是一一對應(yīng)的,其綁定關(guān)系通過一個全局的DIctionary存儲潜支,線程為key甸赃,runloop為value。
  • 線程中的RunLoop主要是用來管理線程的冗酿,當(dāng)線程的RunLoop開啟后埠对,會在執(zhí)行完任務(wù)后進行休眠狀態(tài),當(dāng)有事件觸發(fā)喚醒時裁替,又開始工作项玛,即有活時干活,沒活就休息
  • 主線程的RunLoop是默認開啟的弱判,在程序啟動之后襟沮,會一直運行,不會退出
  • 其他線程的RunLoop默認是不開啟的昌腰,如果需要臣嚣,則手動開啟

RunLoop中涉及到5個重要的類:

CFRunLoop - RunLoop對象
CFRunLoopMode - 五種運行模式
CFRunLoopSource - 輸入源/事件源,包括Source0和Source1
CFRunLoopTimer - 定時源剥哑,也就是NSTimer
CFRunLoopObserve - 觀察者,用來監(jiān)聽RunLoop

CFRunLoopSource - 事件源

  • Source1:基于mach_port和回調(diào)函數(shù)指針淹父,也就是端口通訊株婴,處理來自系統(tǒng)內(nèi)核或其他進程的事件,比如點擊手機屏幕
  • Source0:非基于Port的處理事件暑认,也就是應(yīng)用層事件(內(nèi)部事件困介、APP負責(zé)管理的事件,UIEvent)蘸际,包含一個回調(diào)函數(shù)指針座哩,需要手動標記為待處理或者手動喚醒RunLoop,如performSelector粮彤、block等
    例如:一個APP在前臺靜止根穷,用戶點擊APP界面姜骡,屏幕表面的時事件會先包裝成Event告訴source1(基于mach_port),source1喚醒RunLoop將事件Event分發(fā)給source0屿良,由source0來處理圈澈。

CFRunLooTimer - 定時源
就是NSTimer,在預(yù)設(shè)的時間點喚醒RunLoop執(zhí)行回調(diào)尘惧。因為它是基于RunLoop的康栈,因此它不是實時的(Timer是不準確的,因為RunLoop只負責(zé)分發(fā)源消息喷橙。如果線程當(dāng)前正在處理繁重的任務(wù)啥么,就有可能導(dǎo)致Timer本次延時,或者少執(zhí)行一次)贰逾。

CFRunLoopObserver - 觀察者
用來監(jiān)聽時間點事件CFRunLoopActivity悬荣。

  • KCFRunLoopEntery RunLoop準備啟動
  • kCFRunLoopBeforeTimers RunLoop將要處理一些Timer相關(guān)的事件
  • kCFRunLoopBeforeSources RunLoop將要處理一些Source事件
  • kCFRunLoopBeforeWaiting RunLoop將要進行休眠狀態(tài),即將由用戶狀態(tài)切換內(nèi)核態(tài)
  • kCFRunLoopAfterWaiting RunLoop被喚醒似踱,即從內(nèi)核態(tài)切換到用戶態(tài)
  • kCFRunLoopExit RunLoop退出
  • kCfRunLoopAllActivitires 監(jiān)聽所有狀態(tài)

為什么main函數(shù)能夠保持一直存在且不退出隅熙?
在main函數(shù)內(nèi)容會調(diào)用UIApplication函數(shù),而在UIAPPlicationMain內(nèi)部會啟動主線程的RunLoop核芽,可以做到有消息處理囚戚,能夠迅速從內(nèi)核態(tài)到用戶態(tài)的切換,立刻喚醒處理轧简,而沒有消息處理時驰坊,通過用戶態(tài)到內(nèi)核態(tài)的切換進入等待狀態(tài),避免資源的占用哮独。因此main函數(shù)能夠一直存在并且不退出拳芙。

NSRunLoop 和 CFRunLoopRef 區(qū)別

  • NSRunLoop是基于CFRunLoopRef面向?qū)ο蟮腁PI,是不安全的
  • CFRunLoopRef是基于C語言皮璧,是線程安全的

Runloop的mode作用是什么舟扎?
mode主要是用于指定RunLoop中事件優(yōu)先級的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市悴务,隨后出現(xiàn)的幾起案子睹限,更是在濱河造成了極大的恐慌,老刑警劉巖讯檐,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件羡疗,死亡現(xiàn)場離奇詭異,居然都是意外死亡别洪,警方通過查閱死者的電腦和手機叨恨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挖垛,“玉大人痒钝,你說我怎么就攤上這事秉颗。” “怎么了午乓?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵站宗,是天一觀的道長。 經(jīng)常有香客問我益愈,道長梢灭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任蒸其,我火速辦了婚禮敏释,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘摸袁。我一直安慰自己钥顽,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布靠汁。 她就那樣靜靜地躺著蜂大,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蝶怔。 梳的紋絲不亂的頭發(fā)上奶浦,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天,我揣著相機與錄音踢星,去河邊找鬼澳叉。 笑死,一個胖子當(dāng)著我的面吹牛沐悦,可吹牛的內(nèi)容都是我干的成洗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼藏否,長吁一口氣:“原來是場噩夢啊……” “哼瓶殃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起副签,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤遥椿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后继薛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡愈捅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年遏考,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蓝谨。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡灌具,死狀恐怖青团,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情咖楣,我是刑警寧澤督笆,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站诱贿,受9級特大地震影響娃肿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜珠十,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一料扰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧焙蹭,春花似錦晒杈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至撰豺,卻和暖如春粪般,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背郑趁。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工刊驴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人寡润。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓捆憎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親梭纹。 傳聞我的和親對象是個殘疾皇子躲惰,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,647評論 2 354

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