iOS GCD全析(五)

本文摘錄自《Objective-C高級編程》一書,附加一些自己的理解栋荸,作為對GCD的總結(jié)菇怀。



此篇主要包含以下幾個方面:

  • Dispatch Source
    • dispatch_source_t
    • dispatch_source_create
    • dispatch_source_set_event_handler
    • dispatch_source_resume
    • dispatch_source_cancel
    • dispatch_source_set_timer


dispatch_source

GCD中除了主要的Dispatch Queue外,還有不太引人注目的Dispatch Source晌块。它是BSD系內(nèi)核慣有功能kqueue的包裝爱沟。

kqueue 是在XNU內(nèi)核中發(fā)生各種事件時,在應(yīng)用程序編程方執(zhí)行處理的技術(shù)匆背。其CPU負(fù)荷非常小呼伸,盡量不占用資源。kqueue 可以說是應(yīng)用程序處理XNU內(nèi)核中發(fā)生的各種事件的方法中最優(yōu)秀的一種钝尸。

Dispatch Source可處理以下事件括享。如表所示。

名稱 內(nèi)容
DISPATCH_SOURCE_TYPE_DATA_ADD 變量增加
DISPATCH_SOURCE_TYPE_DATA_OR 變量OR
DISPATCH_SOURCE_TYPE_MACH_SEND MACH端口發(fā)送
DISPATCH_SOURCE_TYPE_MACH_RECV MACH端口接收
DISPATCH_SOURCE_TYPE_READ 可讀取文件映像
DISPATCH_SOURCE_TYPE_WRITE 可寫入文件映像
DISPATCH_SOURCE_TYPE_PROC 監(jiān)測到與進(jìn)程相關(guān)的事件
DISPATCH_SOURCE_TYPE_SIGNAL 接收信號
DISPATCH_SOURCE_TYPE_TIMER 定時器
DISPATCH_SOURCE_TYPE_VNODE 文件系統(tǒng)有變更

事件發(fā)生時珍促,在指定的Dispatch Queue中可執(zhí)行事件的處理铃辖。

下面我們使用DISPATCH_SOURCE_TYPE_READ,異步讀取文件映像猪叙。
const char *fileName = "文件地址";

__block ssize_t total = 0;

/*
 * 打開文件娇斩,獲取文件描述符(open成功則返回文件描述符,否則返回-1)
 */
int fd = open(fileName, O_RDWR);
if (fd == -1) return;

/*
 * 設(shè)定為異步映像
 */
fcntl(fd, F_SETFL, O_NONBLOCK);

/*
 * 獲取用于追加事件處理的 Global Dispatch Queue
 */
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

/*
 * 基于READ事件作成Dispatch Source
 */
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, queue);

if (source == NULL) {
    close(fd);
    return;
}

/*
 * 指定發(fā)生READ事件時執(zhí)行的處理
 */
dispatch_source_set_event_handler(source, ^{
    /*
     * 預(yù)估要讀取的字節(jié)數(shù)
     *
     * dispatch_source_get_data()函數(shù)的返回值
     * 要根據(jù)dispatch_source_create()創(chuàng)建source時所選的類型而定穴翩,
     *
     * DISPATCH_SOURCE_TYPE_READ: estimated bytes available to read
     *
     * DISPATCH_SOURCE_TYPE_TIMER: number of times the timer has 
     * fired since the last handler invocation
     */
    size_t estimatedSize = dispatch_source_get_data(source);

    /*
     * 這個buff就是向堆申請的一塊內(nèi)存犬第,用來暫時緩存文件映像
     * 參數(shù)是你要申請的內(nèi)存大小,使用dispatch_source_get_data獲取到的
     * 是整個文件的大小芒帕,也可以一段一段讀取瓶殃,將參數(shù)寫成   
     * size_t estimatedSize = 1024; 
     * 這個1024可以根據(jù)項目需要定義成其它數(shù)字,如100副签、1000、10000...
     */
    void *buff = malloc(estimatedSize);
    
    if (buff) {
        
        /*
         * 從映像中讀取
         */
        ssize_t length = read(fd, buff, estimatedSize);
        total += length;
        
        /*
         * buff的處理
         *
         * buff可以讀取任何文件基矮,包括多媒體文件淆储。通過NSData可進(jìn)行轉(zhuǎn)換。
         *
         * NSLog有個系統(tǒng)bug家浇,打印不完整本砰。printf可以打印完整。UITextView也可以呈現(xiàn)完整钢悲。
         */
        
        NSData *data = [NSData dataWithBytes:buff length:length];
        
        NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
//            printf("%s\n=============\n", [text UTF8String]);
        
        dispatch_async(dispatch_get_main_queue(), ^{
            UITextView *textView = [[UITextView alloc] initWithFrame:self.view.bounds];
            [self.view addSubview:textView];
            textView.text = text;
        });
        free(buff);
        
        /*
         * 此處我設(shè)置的buff緩存區(qū)大小正好就是文件大小点额,
         * 所以直接調(diào)用 dispatch_source_cancel(source) 讓它直接結(jié)束舔株。
         * 因為這次讀取的就是全部,不必再循環(huán)調(diào)用方法去讀取了还棱。
         */
        dispatch_source_cancel(source);
    }
});

dispatch_source_set_cancel_handler(source, ^{
    close(fd);
    NSLog(@"%zd", total);
});

/*
 * 啟動Dispatch Source
 */

dispatch_resume(source);

上面這段代碼是參考了《Objective-C高級編程》和網(wǎng)上各位大神的博文载慈,我做了整合后整理出的demo。

《Objective-C高級編程》書中的代碼片段是一個大致思想珍手,而各類博文中大多將方法拆分办铡,看起來很高深(本人水平有限??,看網(wǎng)上大神的文章好多時候都是直撓頭)琳要。用網(wǎng)上的源代碼在自己的工程里運行的時候寡具,有很多NSLog都是null,而按照《Objective-C高級編程》中的方式雖然沒有null稚补,但是打印不全童叠。于是找到了問題所在,不是代碼有問題课幕,而是NSLog本身存在問題:大段的字符串打印不完全厦坛。使用printf代替就會打印完整,而我用了一個更直觀的替代方式撰豺,把字符串用UITextView展示出來粪般,這樣就很容易看了。

順便我去蘋果官方文檔看了一下dispatch_source_get_data函數(shù)究竟是怎么回事污桦,這是原文鏈接亩歹,我在上面的源碼中帶了注釋,dispatch_source_get_data函數(shù)的用途或者說返回值要視情況而定凡橱,這要看你創(chuàng)建dispatch_source_t的時候選了什么參數(shù)類型小作,就是下面這句中的DISPATCH_SOURCE_TYPE_READ

dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, queue);

使用DISPATCH_SOURCE_TYPE_READ創(chuàng)建source時,dispatch_source_get_data函數(shù)的返回值就是 estimated bytes available to read 即預(yù)估的可讀取字節(jié)數(shù)稼钩。但是我一開始還是用的迷糊了顾稀,因為dispatch_source_get_data函數(shù)的調(diào)用時機不能太隨意,一定要在dispatch_source_set_event_handler函數(shù)的回調(diào)中才會有效坝撑【哺眩《Objective-C高級編程》是這樣寫的代碼片段,而我自己實踐后發(fā)現(xiàn)確實是這樣??巡李!

這里面還有一個坑就是buff抚笔,實際上buff代表的是你申請的一塊堆內(nèi)存,而buff本身是這塊內(nèi)存的首地址侨拦。讓人迷糊的就是dispatch_source_set_event_handler函數(shù)的block自身會循環(huán)多次調(diào)用殊橙,直到你強制停止為止。

  • 當(dāng)buff申請的大小正好是文件大小的時候,dispatch_source_set_event_handler函數(shù)不會重復(fù)執(zhí)行膨蛮,只執(zhí)行一次就會結(jié)束叠纹。

  • 當(dāng)buff申請的大小比文件小的時候,就會不停的循環(huán)調(diào)用敞葛,直到所有文件內(nèi)存讀取完畢才會停止調(diào)用誉察,或者你提前直接調(diào)用dispatch_source_cancel強制其停止。


下面我們使用DISPATCH_SOURCE_TYPE_TIMER實現(xiàn)一個定時器
// 我假裝搞了一個每秒執(zhí)行一次制肮,執(zhí)行10次就停止的定時器

static dispatch_source_t timer;
static int count = 0;
- (void)startTimer {
    
    timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
    
    /*
     * 設(shè)置定時器
     * 第一個參數(shù):定時器(dispatch_source_t)
     * 第二個參數(shù):定時器時間起點
     * 第三個參數(shù):定時器時間間隔
     * 第四個參數(shù):定時器允許延遲執(zhí)行的時間
     */
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC, 0ull * NSEC_PER_SEC);
    
    /*
     * 指定定時器啟動后要執(zhí)行的任務(wù)
     */
    dispatch_source_set_event_handler(timer, ^{
        if (count++ < 10) {
            NSLog(@"定時器任務(wù)");
        }
        else {
            dispatch_source_cancel(timer);
        }
    });
    
    /*
     * 指定取消 Dispatch Source 時的處理
     */
    dispatch_source_set_cancel_handler(timer, ^{
        NSLog(@"定時器結(jié)束咯冒窍!");
    });
    
    dispatch_resume(timer);
}

相對于文件映像的異步讀取來說,定時器就變得簡單的很了豺鼻,我能想到的都寫在源碼的注釋里了综液,就不多說了。

看了異步讀取文件映像用的源代碼和這個定時器用的源代碼后儒飒,有沒有注意到什么呢?實際上Dispatch Queue沒有“取消”這一概念谬莹。一旦將處理追加到Dispatch Queue中,就沒有方法可將該處理去除桩了,也沒有方法可在執(zhí)行中取消該處理附帽。編程人員要么在處理中導(dǎo)入取消這一概念,要么放棄取消井誉,或者使用NSOperationQueue等其他方法蕉扮。

Dispatch Source與Dispatch Queue不同,是可以取消的颗圣。而且取消時必須執(zhí)行的處理可指定為回調(diào)用的Block形式喳钟。因此使用Dispatch Source實現(xiàn)XNU內(nèi)核中發(fā)生的事件處理要比直接使用kqueue 實現(xiàn)更為簡單。在必須使用kqueue的情況下希望大家還是使用Dispatch Source,它比較簡單在岂。

那么Dispatch Source 與 Dispatch Queue 兩者在線程執(zhí)行上的是什么關(guān)系奔则?

答案是:沒有關(guān)系。兩者會獨立運行蔽午。 Dispatch Queue 像一個生產(chǎn)任務(wù)的生產(chǎn)者易茬,而 Dispatch Source 像處理任務(wù)的消費者〖袄希可以一邊異步生產(chǎn)抽莱,也可一邊異步消費。你可以在任意線程上調(diào)用 dispatch_source_merge_data 以觸發(fā) dispatch_source_set_event_handler 骄恶。而句柄的執(zhí)行線程岸蜗,取決于你創(chuàng)建句柄時所指定的線程,如果你像下面這樣創(chuàng)建叠蝇,那么句柄會在主線程執(zhí)行:



下面我們介紹DISPATCH_SOURCE_TYPE_DATA_ADD相關(guān)用法

分派源提供了高效的方式來處理事件。首先注冊事件處理程序,事件發(fā)生時會收到通知悔捶。如果在系統(tǒng)還沒有來得及通知你之前事件就發(fā)生了多次铃慷,那么這些事件會被合并為一個事件。這對于底層的高性能代碼很有用蜕该,但是OS應(yīng)用開發(fā)者很少會用到這樣的功能犁柜。類似地,分派源可以響應(yīng)UNIX信號堂淡、文件系統(tǒng)的變化馋缅、其他進(jìn)程的變化以及Mach Port事件。它們中很多都在Mac系統(tǒng)上很有用绢淀,但是iOS開發(fā)者通常不會用到萤悴。

不過,自定義源在iOS中很有用皆的,尤其是在性能至關(guān)重要的場合進(jìn)行進(jìn)度反饋覆履。使用時,首先創(chuàng)建一個源:自定義源累積事件中傳遞過來的值费薄。累積方式可以是相加(DISPATCH_SOURCE_TYPE_DATA_ADD), 也可以是邏輯或(DISPATCH_SOURCE_DATA_OR)硝全。自定義源也需要一個隊列,用來處理所有的響應(yīng)處理塊楞抡。

創(chuàng)建源后伟众,需要提供相應(yīng)的處理方法。當(dāng)源生效時會分派注冊處理方法;當(dāng)事件發(fā)生時會分派事件處理方法;當(dāng)源被取消時會分派取消處理方法召廷。

在同一時間凳厢,只有一個處理方法塊的實例被分派。如果這個處理方法還沒有執(zhí)行完畢柱恤,另一個事件就發(fā)生了数初,事件會以指定方式(ADD或者OR)進(jìn)行累積。通過合并事件的方式梗顺,系統(tǒng)即使在高負(fù)載情況下也能正常工作泡孩。當(dāng)處理事件件被最終執(zhí)行時,計算后的數(shù)據(jù)可以通過 dispatch_source_get_data 來獲取寺谤。這個數(shù)據(jù)的值在每次響應(yīng)事件執(zhí)行后會被重置仑鸥。

讓GCD像OperationQueue那樣暫停任務(wù)

demo的原作者微博@iOS程序犭袁


- (void)viewDidLoad {
    //1.
    // 指定DISPATCH_SOURCE_TYPE_DATA_ADD,做成Dispatch Source(分派源)变屁。設(shè)定Main Dispatch Queue 為追加處理的Dispatch Queue
    _processingQueueSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0,dispatch_get_main_queue());
    
    __block NSUInteger totalComplete = 0;
    
    dispatch_source_set_event_handler(_processingQueueSource, ^{
        //當(dāng)處理事件被最終執(zhí)行時眼俊,dispatch_source_get_data獲取的值是dispatch_source_merge_data第二個參數(shù)傳過來的值。這個數(shù)據(jù)的值在每次響應(yīng)事件執(zhí)行后會被重置粟关,所以totalComplete的值是最終累積的值疮胖。
        NSUInteger value = dispatch_source_get_data(self->_processingQueueSource);
        totalComplete += value;
        NSLog(@"進(jìn)度:%.2f", totalComplete/1000.0);
    });
    
    //分派源創(chuàng)建時默認(rèn)處于暫停狀態(tài),在分派源分派處理程序之前必須先恢復(fù)。
    dispatch_resume(_processingQueueSource);
    self.running = YES;
    
    //2.
    //恢復(fù)源后澎灸,就可以通過dispatch_source_merge_data向Dispatch Source(分派源)發(fā)送事件:
    _queue = dispatch_queue_create("com.example", DISPATCH_QUEUE_SERIAL);
    for (NSUInteger index = 0; index < 1000; index++) {
        dispatch_async(_queue, ^{
            if (!self.running) {
                return;
            }
            // 調(diào)用dispatch_source_merge_data以觸發(fā)dispatch_source_set_event_handler回調(diào)
            dispatch_source_merge_data(self->_processingQueueSource, 1);
            usleep(10000);//0.01秒
        });
    }
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    [self changeStatus:self.running];
}
- (void)changeStatus:(BOOL)shouldPause {
    if (shouldPause) {
        [self pause];
    } else {
        [self resume];
    }
}
- (void)resume {
    if (self.running) {
        return;
    }
    NSLog(@"?恢復(fù)Dispatch Source(分派源)以及_queue");
    self.running = YES;
    dispatch_resume(_processingQueueSource);
    if (_queue) {
        dispatch_resume(_queue);
    }
}

- (void)pause {
    if (!self.running) {
        return;
    }
    NSLog(@"??暫停Dispatch Source(分派源)以及_queue");
    self.running = NO;
    dispatch_suspend(_processingQueueSource);
    dispatch_suspend(_queue);
}

運行結(jié)果:


且慢院塞,寡人有一問

原作者微博@iOS程序犭袁在給隊列添加任務(wù)的時候,使用的是串行隊列性昭。為什么不使用并發(fā)隊列而是使用串行隊列呢拦止?

之前的《iOS GCD全析(四)》說過,dispatch_suspend 可以把GCD的隊列(queue)和派發(fā)源(source)掛起糜颠。但是掛起隊列有個問題就是即便調(diào)用dispatch_suspend汹族,任務(wù)也不會立即停止,在調(diào)用dispatch_suspend之前已經(jīng)開始執(zhí)行的任務(wù)不會受到影響其兴,仍然會繼續(xù)執(zhí)行顶瞒。而在調(diào)用dispatch_suspend之后還未開始執(zhí)行的任務(wù)會受到影響,暫時不執(zhí)行忌警,進(jìn)入暫停狀態(tài)搁拙。

我們知道,串行隊列中任務(wù)的特點就是隊列中的任務(wù)會一個接一個執(zhí)行法绵,而并發(fā)隊列中的任務(wù)在異步添加后會并發(fā)執(zhí)行箕速。demo中 for 循環(huán)1000次,也就是向串行隊列添加了1000個任務(wù)朋譬,這1000個任務(wù)要一個接一個執(zhí)行盐茎。

再看demo中的暫停方法 - (void)pause ,方法中先掛起了source

dispatch_suspend(_processingQueueSource);

然后再掛起了queue徙赢。這樣在暫停的時候字柠,就是先讓source的回調(diào)先停下來,

dispatch_source_set_event_handler {
    ...
}

再讓queue中的任務(wù)停下來狡赐。微博@iOS程序犭袁使用串行隊列的好處就在這里窑业,在隊列掛起后,因為 dispatch_suspend 不能約束已經(jīng)開始執(zhí)行的任務(wù)枕屉,所以最多有一個任務(wù)在執(zhí)行中常柄,也就是說 “不聽 dispatch_suspend 使喚的任務(wù)” 最多只有這一個尚在執(zhí)行中的任務(wù)。

在恢復(fù)方法- (void)resume中搀擂,則是先恢復(fù)了source西潘,然后恢復(fù)了queue。這樣一來哨颂,就在source已經(jīng)恢復(fù)而queue還未恢復(fù)的這個時機喷市,source的 dispatch_source_set_event_handler 回調(diào)有時間去處理那個 “不聽 dispatch_suspend 使喚的任務(wù)” 那一次調(diào)用的 dispatch_source_merge_data,再讓queue中的任務(wù)繼續(xù)威恼。如此一來品姓,隊列queue和派發(fā)源source的暫停與恢復(fù)就會在表面上看起來真正的實現(xiàn)了寝并,沒有什么問題。

但是假如我們換成并發(fā)隊列缭黔,情況就會變得很不一樣食茎。在調(diào)用 dispatch_suspend 想要暫停時,由于多個任務(wù)并發(fā)調(diào)用 dispatch_source_merge_data 馏谨,這就造成 “不聽 dispatch_suspend 使喚的任務(wù)” 會有很多,而 dispatch_source_set_event_handler 回調(diào)又先行暫停附迷,不能處理惧互,這些任務(wù)調(diào)用的 dispatch_source_merge_data 就會先積壓下。

在恢復(fù)的時候喇伯,dispatch_source_set_event_handler 回調(diào)會先去處理積壓的 dispatch_source_merge_data 調(diào)用喊儡,這些調(diào)用會被合并,只執(zhí)行一次稻据。這樣一來艾猜,在一次暫停繼而恢復(fù)后,打印的進(jìn)度就會缺失一些捻悯,因為有些被合并執(zhí)行了匆赃。

細(xì)心的讀者可能已經(jīng)能發(fā)現(xiàn)了,如果換成并發(fā)隊列今缚,即便是不暫停算柳,也一樣會造成打印的進(jìn)度有缺失的情況。因為多個 dispatch_source_merge_data 一起調(diào)用姓言,根據(jù)source的特點瞬项,一定會被合并執(zhí)行。

下面是換成并發(fā)隊列的打印結(jié)果:

進(jìn)度:0.128
進(jìn)度:0.131
進(jìn)度:0.133
進(jìn)度:0.137
進(jìn)度:0.138
進(jìn)度:0.139
進(jìn)度:0.140
進(jìn)度:0.141
進(jìn)度:0.142
進(jìn)度:0.152
...
進(jìn)度:0.955
進(jìn)度:0.957
進(jìn)度:0.960
進(jìn)度:0.962
進(jìn)度:0.966
進(jìn)度:0.967
進(jìn)度:1.000

本章參考:

Parse源碼淺析系列(一)---Parse的底層多線程處理思路:GCD高級用法

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末何荚,一起剝皮案震驚了整個濱河市囱淋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌餐塘,老刑警劉巖妥衣,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異唠倦,居然都是意外死亡称鳞,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門稠鼻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冈止,“玉大人,你說我怎么就攤上這事候齿∥醣” “怎么了闺属?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長周霉。 經(jīng)常有香客問我瞎暑,道長,這世上最難降的妖魔是什么茁彭? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任些椒,我火速辦了婚禮,結(jié)果婚禮上狞谱,老公的妹妹穿的比我還像新娘乃摹。我一直安慰自己,他們只是感情好跟衅,可當(dāng)我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布孵睬。 她就那樣靜靜地躺著,像睡著了一般伶跷。 火紅的嫁衣襯著肌膚如雪掰读。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天叭莫,我揣著相機與錄音蹈集,去河邊找鬼。 笑死食寡,一個胖子當(dāng)著我的面吹牛雾狈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播抵皱,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼善榛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了呻畸?” 一聲冷哼從身側(cè)響起移盆,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎伤为,沒想到半個月后咒循,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡绞愚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年叙甸,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片位衩。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡裆蒸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出糖驴,到底是詐尸還是另有隱情僚祷,我是刑警寧澤佛致,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站辙谜,受9級特大地震影響俺榆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜装哆,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一罐脊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蜕琴,春花似錦爹殊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽层玲。三九已至号醉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間辛块,已是汗流浹背畔派。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留润绵,地道東北人线椰。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像尘盼,于是被迫代替她去往敵國和親憨愉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,927評論 2 355

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

  • package main 參考文檔字符串操作
    小歪子go閱讀 184評論 0 0
  • 圖文 \ 海馬 《走神的藝術(shù)與科學(xué)》是一本很有趣的科普書卿捎,這本書的作者是新西蘭的邁克爾·C·科爾巴里斯配紫,他不僅是位...
    叮叮兔海馬閱讀 632評論 0 0
  • 天上好多的星星 海里好多的鯊魚 我一個也沒靠近看見過 遠(yuǎn)遠(yuǎn)的遠(yuǎn)遠(yuǎn)的 我也說不清楚 他們是不是存在著 望不到頭的無盡...
    南道海無風(fēng)閱讀 175評論 0 0
  • 所謂說話,就是交流午阵。第一要務(wù)是聽別人是不是需要你躺孝,第二是告訴對方你能不能干。所以說固然重要底桂,會聽才是更重要的植袍。 用...
    遇見未來的我閱讀 455評論 0 0