探索 Block 的本質(zhì)

定義

  • Block 是 C 語言的擴(kuò)充功能
  • Block 是帶有自動(dòng)變量(局部變量)的匿名函數(shù)

本質(zhì)

  • Block 是一個(gè) Objc 對象

底層實(shí)現(xiàn)

下面我將通過一個(gè)簡單的例子绿贞,結(jié)合源代碼進(jìn)行介紹

int main(int argc, const char * argv[]) {
    void (^blk)(void) = ^{ printf("Hello Block\n"); };
    blk();
    return 0;
}

使用clang -rewrite-objc main.m炎疆,我們可以將 Objc 的源碼轉(zhuǎn)成 Cpp 的相關(guān)源碼:

int main(int argc, const char * argv[]) {
    // Block 的創(chuàng)建
    void (*blk)(void) =
        (void (*)(void))&__main_block_impl_0(
            (void *)__main_block_func_0, &__main_block_desc_0_DATA);
    
    // Block 的使用
    ((void (*)(struct __block_impl *))(
        (struct __block_impl *)blk)->FuncPtr)((struct __block_impl *)blk);
    return 0;
}

由上面的源碼,我們能猜想到:

  • Block 的創(chuàng)建涉及__main_block_impl_0結(jié)構(gòu)體
  • Block 的涉及到了FuncPtr函數(shù)指針的調(diào)用

從這里為切入點(diǎn)看看上面提到的都是啥

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

Block 的真身:

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    // 省略了構(gòu)造函數(shù)
};
  • Block 其實(shí)不是一個(gè)匿名函數(shù),他是一個(gè)結(jié)構(gòu)體
  • __main_block_impl_0名字的命名規(guī)則: __所在函數(shù)_block_impl_序號

impl 變量的數(shù)據(jù)結(jié)構(gòu)

__main_block_impl_0的主要數(shù)據(jù):

struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};
  • isa指針: 體現(xiàn)了 Block 是 Objc 對象的本質(zhì)刑然。
  • FuncPtr指針: 其實(shí)就是一個(gè)函數(shù)指針缔刹,指向所謂的匿名函數(shù)。

Desc 變量的數(shù)據(jù)結(jié)構(gòu)

__main_block_desc_0中放著 Block 的描述信息

static struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
} __main_block_desc_0_DATA = {
    0,
    sizeof(struct __main_block_impl_0)
};

"匿名函數(shù)"

__main_block_impl_0即 Block 創(chuàng)建時(shí)候使用到了__main_block_func_0正是下面的函數(shù):

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    printf("Hello Block\n");
}
  • 這部分和^{ printf("Hello Block\n"); }十分相似杀迹,由此可看出: 通過 Blocks 使用的匿名函數(shù)實(shí)際上被作為簡單的 C 語言函數(shù)來處理
  • 函數(shù)名是根據(jù) Block 語法所屬的函數(shù)名(此處main)和該 Block 語法在函數(shù)出現(xiàn)的順序值(此處為 0)來命名的亡脸。
  • 函數(shù)的參數(shù)__cself相當(dāng)于 C++ 實(shí)例方法中指向?qū)嵗陨淼淖兞?code>this,或是 Objective-C 實(shí)例方法中指向?qū)ο笞陨淼淖兞?code>self树酪,即參數(shù)__cself為指向 Block 的變量浅碾。
  • 上面的(*blk->impl.FuncPtr)(blk);中的blk就是__cself

介紹了基本的數(shù)據(jù)結(jié)構(gòu),下面到回到一開始的main函數(shù)续语,看看 Block 具體的使用

Block 的創(chuàng)建

void (*blk)(void) =
        (void (*)(void))&__main_block_impl_0(
            (void *)__main_block_func_0, &__main_block_desc_0_DATA);
/** 去掉轉(zhuǎn)換的部分
 struct __main_block_impl_0 tmp =
     __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
 struct __main_block_impl_0 *blk = &tmp;
*/
  • void (^blk)(void)就是是一個(gè)struct __main_block_impl_0 *blk
  • Block 表達(dá)式的其實(shí)就是通過所謂的匿名函數(shù)__main_block_func_0的函數(shù)指針創(chuàng)建一個(gè)__main_block_impl_0結(jié)構(gòu)體垂谢,我們用的時(shí)候是拿到了這個(gè)結(jié)構(gòu)體的指針。

Block 的使用

((void (*)(struct __block_impl *))(
        (struct __block_impl *)blk)->FuncPtr)((struct __block_impl *)blk);
/** 去掉轉(zhuǎn)換的部分
 (*blk->impl.FuncPtr)(blk);
*/
  • Block 真正的使用方法就是使用__main_block_impl_0中的函數(shù)指針FuncPtr
  • (blk)這里是傳入自己疮茄,就是給_cself傳參

Block 的類型

從 Block 中的簡單實(shí)現(xiàn)中滥朱,我們從isa中發(fā)現(xiàn) Block 的本質(zhì)是 Objc 對象根暑,是對象就有不同類型的類。因此徙邻,Block 當(dāng)然有不同的類型

在 Apple 的libclosure-73中的data.c上可見排嫌,isa可指向:

void * _NSConcreteStackBlock[32] = { 0 }; // 棧上創(chuàng)建的block
void * _NSConcreteMallocBlock[32] = { 0 }; // 堆上創(chuàng)建的block
void * _NSConcreteAutoBlock[32] = { 0 };
void * _NSConcreteFinalizingBlock[32] = { 0 };
void * _NSConcreteGlobalBlock[32] = { 0 }; // 作為全局變量的block
void * _NSConcreteWeakBlockVariable[32] = { 0 };

其中我們最常見的是:

Block的類型 名稱 行為 存儲(chǔ)位置
_NSConcreteStackBlock 棧Block 捕獲了局部變量
_NSConcreteMallocBlock 堆Block 對棧Block調(diào)用copy所得
_NSConcreteGlobalBlock 全局Block 定義在全局變量中 常量區(qū)(數(shù)據(jù)段)

PS: 內(nèi)存五大區(qū):棧、堆缰犁、靜態(tài)區(qū)(BSS 段)淳地、常量區(qū)(數(shù)據(jù)段)、代碼段

關(guān)于 copy 操作

對象有copy操作帅容,Block 也有copy操作薇芝。不同類型的 Block 調(diào)用copy操作,也會(huì)產(chǎn)生不同的復(fù)制效果:

Block的類型 副本源的配置存儲(chǔ)域 復(fù)制效果
_NSConcreteStackBlock 從棧復(fù)制到堆
_NSConcreteGlobalBlock 常量區(qū)(數(shù)據(jù)段) 什么也不做
_NSConcreteMallocBlock 引用計(jì)數(shù)增加

棧上的 Block 復(fù)制到堆上的時(shí)機(jī)

  • 調(diào)用 Block 的copy實(shí)例方法

編譯器自動(dòng)調(diào)用_Block_copy函數(shù)情況

  • Block 作為函數(shù)返回值返時(shí)
  • 將 Block 賦值給 __strong 指針(id或 Block 類型成員變量)
  • 在 Apple 的 Cocoa丰嘉、GCD 等 api 中傳遞 Block 時(shí)

PS: 在 ARC 環(huán)境下夯到,聲明的 Block 屬性用copystrong修飾的效果是一樣的,但在 MRC 環(huán)境下用 copy 修飾饮亏。

捕獲變量

基礎(chǔ)類型變量

以全局變量耍贾、靜態(tài)全局變量、局部變量路幸、靜態(tài)局部變量為例:

int global_val = 1;
static int static_global_val = 2;

int main(int argc, const char * argv[]) {
    int val = 3;
    static int static_val = 4;
    
    void (^blk)(void) = ^{
        printf("global_val is %d\n", global_val);
        printf("static_global_val is %d\n", static_global_val);
        printf("val is %d\n", val);
        printf("static_val is %d\n", static_val);
    };
    
    blk();
    
    return 0;
}

轉(zhuǎn)換后“匿名函數(shù)”對應(yīng)的代碼:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    int val = __cself->val; // bound by copy
    int *static_val = __cself->static_val; // bound by copy

    printf("global_val is %d\n", global_val);
    printf("static_global_val is %d\n", static_global_val);
    printf("val is %d\n", val);
    printf("static_val is %d\n", (*static_val));
}
  • 全局變量荐开、靜態(tài)全局變量: 作用域?yàn)槿郑虼嗽?Block 中是直接訪問的简肴。
  • 局部變量: 生成的__main_block_impl_0中存在val實(shí)例晃听,因此對于局部變量,Block 只是單純的復(fù)制創(chuàng)建時(shí)候局部變量的瞬時(shí)值砰识,我們可以使用值能扒,但不能修改值。
struct __main_block_impl_0 {
  // ...
  int val; // 值傳遞
  // ...
};
  • 靜態(tài)局部變量: 生成的__main_block_impl_0中存在static_val指針辫狼,因此 Block 是在創(chuàng)建的時(shí)候獲取靜態(tài)局部變量的指針值初斑。
struct __main_block_impl_0 {
    // ...
    int *static_val; // 指針傳遞
    // ...
};

對象類型變量

模仿基礎(chǔ)類型變量,實(shí)例化四個(gè)不一樣的SCPeople變量:

int main(int argc, const char * argv[]) {
    // 省略初始化
    [globalPeople introduce];
    [staticGlobalPeople introduce];
    [people introduce];
    [staticPeople introduce];
    
    return 0;
}

轉(zhuǎn)換后"匿名函數(shù)"對應(yīng)的代碼:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    SCPeople *people = __cself->people; // bound by copy
    SCPeople **staticPeople = __cself->staticPeople; // bound by copy

    // 省略 objc_msgSend 轉(zhuǎn)換
    [globalPeople introduce];
    [staticGlobalPeople introduce];
    [people introduce];
    [*staticPeople introduce];
}
  • 全局對象膨处、靜態(tài)全局對象: 作用域依然是全局见秤,因此在 Block 中是直接訪問的。
  • 局部對象: 生成的__main_block_impl_0中存在people指針實(shí)例真椿,因此 Block 獲取的是指針?biāo)查g值鹃答,我們可以在 Block 中通過指針可以操作對象,但是不能改變指針的值突硝。
struct __main_block_impl_0 {
    // ...
    SCPeople *people;
    // ...
};
  • 靜態(tài)局部對象: 生成的__main_block_impl_0中存在staticPeople指針的指針测摔,因此 Block 是在創(chuàng)建的時(shí)候獲取靜態(tài)局部對象的指針值(即指針的指針)。
struct __main_block_impl_0 {
    // ...
    SCPeople **staticPeople;
    // ...
};

小結(jié)

通過對基礎(chǔ)類型狞换、對象類型與四種不同的變量進(jìn)行排列組合的小 Demo避咆,不難得出下面的規(guī)則:

變量類型 是否捕獲到 Block 內(nèi)部 訪問方式
全局變量 直接訪問
靜態(tài)全局變量 直接訪問
局部變量 值訪問
靜態(tài)局部變量 指針訪問

PS:

  • 基礎(chǔ)類型和對象指針類型其實(shí)是一樣的舟肉,只不過指針的指針看起來比較繞而已修噪。
  • 全局變量與靜態(tài)全局變量的存儲(chǔ)方式查库、生命周期是相同的。但是作用域不同黄琼,全局變量在所有文件中都可以訪問到樊销,而靜態(tài)全局變量只能在其申明的文件中才能訪問到。

變量修改

上面的篇幅通過底層實(shí)現(xiàn)脏款,向大家介紹了 Block 這個(gè)所謂"匿名函數(shù)"是如何捕獲變量的围苫,但是一些時(shí)候我們需要修改 Block 中捕獲的變量:

修改全局變量或靜態(tài)全局變量

全局變量與靜態(tài)全局變量的作用域都是全局的,自然在 Block 內(nèi)外的變量操作都是一樣的撤师。

修改靜態(tài)局部變量

在上面變量捕獲的章節(jié)中剂府,我們得知 Block 捕獲的是靜態(tài)局部變量的指針值,因此我們可以在 Block 內(nèi)部改變靜態(tài)局部變量的值(底層是通過指針來進(jìn)行操作的)剃盾。

修改局部變量

使用__block修飾符來指定我們想改變的局部變量腺占,達(dá)到在 Block 中修改的需要。

我們用同樣的方式痒谴,通過底層實(shí)現(xiàn)認(rèn)識一下__block衰伯,舉一個(gè)??:

 __block int val = 0;
void (^blk)(void) = ^{ val = 1; };
blk();

經(jīng)過轉(zhuǎn)換的代碼中出現(xiàn)了和單純捕獲局部變量不同的代碼:

__Block_byref_val_0結(jié)構(gòu)體

struct __Block_byref_val_0 {
    void *__isa; // 一個(gè) Objc 對象的體現(xiàn)
    __Block_byref_val_0 *__forwarding; // 指向該實(shí)例自身的指針
    int __flags;
    int __size;
    int val; // 原局部變量
};
  • 編譯器會(huì)將__block修飾的變量包裝成一個(gè) Objc 對象。

val轉(zhuǎn)換成__Block_byref_val_0

__attribute__((__blocks__(byref))) __Block_byref_val_0 val = {
    (void*)0,
    (__Block_byref_val_0 *)&val,
    0,
    sizeof(__Block_byref_val_0),
    0
};

__main_block_impl_0捕獲的變量

struct __main_block_impl_0 {
    // ...
    __Block_byref_val_0 *val; // by ref
    // ...
};
  • Block的__main_block_impl_0結(jié)構(gòu)體實(shí)例持有指向__block變量的__Block_byref_val_0結(jié)構(gòu)體實(shí)例的指針积蔚。
  • 這個(gè)捕獲方式和捕獲靜態(tài)局部變量相似意鲸,都是指針傳遞

"匿名函數(shù)"的操作

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    __Block_byref_val_0 *val = __cself->val; // bound by ref
    
    (val->__forwarding->val) = 1;
}

(val->__forwarding->val) 解釋

  • 左邊的val__main_block_impl_0中的val,這個(gè)val通過__block int val的地址初始化
  • 右邊的val__Block_byref_val_0中的val尽爆,正是__block int valval
  • __forwarding在這里只是單純指向了自己而已
    val->__forwarding->val

__forwarding 的存在意義

上面的"棧Blcok"中__forwarding在這里只是單純指向自己怎顾,但是在當(dāng)"棧Blcok"復(fù)制變成"堆Block"后,__forwarding就有他的存在意義了:

__forwarding 的存在意義

PS:__block修飾符不能用于修飾全局變量漱贱、靜態(tài)變量杆勇。

內(nèi)存管理

Block 與對象類型

copy & dispose

眾所周知,對象其實(shí)也是使用一個(gè)指針指向?qū)ο蟮拇鎯?chǔ)空間饱亿,我們的對象值其實(shí)也是指針值蚜退。雖然是看似對象類型的捕獲與基礎(chǔ)類型的指針類型捕獲差不多,但是捕獲對象的轉(zhuǎn)換代碼比基礎(chǔ)指針類型的轉(zhuǎn)換代碼要多彪笼。(__block變量也會(huì)變成一個(gè)對象钻注,因此下面的內(nèi)容也適用于__block修飾局部變量的情況)。多出來的部分是與內(nèi)存管理相關(guān)的copy函數(shù)與dispose函數(shù):

底層實(shí)現(xiàn)

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
    _Block_object_assign((void*)&dst->people, (void*)src->people, 3/*BLOCK_FIELD_IS_OBJECT*/);
    _Block_object_assign((void*)&dst->staticPeople, (void*)src->staticPeople, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {
    _Block_object_dispose((void*)src->people, 3/*BLOCK_FIELD_IS_OBJECT*/);
    _Block_object_dispose((void*)src->staticPeople, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

這兩個(gè)函數(shù)在 Block 數(shù)據(jù)結(jié)構(gòu)存在于Desc變量中:

static struct __main_block_desc_0 {
  // ...
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
}; // 省略了初始化好的結(jié)構(gòu)體

函數(shù)調(diào)用時(shí)機(jī)

函數(shù) 調(diào)用時(shí)機(jī)
copy 函數(shù) 棧上的 Block 復(fù)制到堆時(shí)
dispose 函數(shù) 堆上的 Block 被廢棄時(shí)

函數(shù)意義

  • copy函數(shù)中的_Block_object_assign函數(shù)相當(dāng)于內(nèi)存管理中的retain函數(shù)配猫,將對象賦值在對象類型的結(jié)構(gòu)體成員變量中幅恋。
  • dispose函數(shù)中的_Block_object_dispose函數(shù)相當(dāng)于內(nèi)存管理中的release函數(shù),釋放賦值在對象類型的結(jié)構(gòu)體變量中的對象泵肄。
  • 通過copydispose并配合 Objc 運(yùn)行時(shí)庫對其的調(diào)用可以實(shí)現(xiàn)內(nèi)存管理

※ 例子

當(dāng) Block 內(nèi)部訪問了對象類型的局部變量時(shí):

  • 當(dāng) Block 存儲(chǔ)在棧上時(shí): Block 不會(huì)對局部變量產(chǎn)生強(qiáng)引用捆交。
  • 當(dāng) Block 被copy到堆上時(shí): Block 會(huì)調(diào)用內(nèi)部的copy函數(shù)淑翼,copy函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_assign函數(shù),_Block_object_assign函數(shù)會(huì)根據(jù)局部變量的修飾符(__strong品追、__weak玄括、__unsafe_unretained)作出相應(yīng)的內(nèi)存管理操作。(注意: 多個(gè) Block 對同一個(gè)對象進(jìn)行強(qiáng)引用的時(shí)肉瓦,堆上只會(huì)存在一個(gè)該對象)
  • 當(dāng) Block 從堆上被移除時(shí): Block 會(huì)調(diào)用內(nèi)部的dispose函數(shù)遭京,dispose函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_dispose函數(shù),_Block_object_dispose函數(shù)會(huì)自動(dòng)release引用的局部變量泞莉。(注意: 直到被引用的對象的引用計(jì)數(shù)為 0哪雕,這個(gè)堆上的該對象才會(huì)真正釋放)

PS: 對于__block變量,Block 永遠(yuǎn)都是對__Block_byref_局部變量名_0進(jìn)行強(qiáng)引用鲫趁。如果__block修飾符背后還有其他修飾符斯嚎,那么這些修飾符是用于修飾__Block_byref_局部變量名_0中的局部變量的。

現(xiàn)象: Block 中使用的賦值給附有__strong修飾符的局部變量的對象和復(fù)制到堆上的__block變量由于被堆的 Block 所持有挨厚,因而可超出其變量作用域而存在堡僻。

循環(huán)引用

由于 Block 內(nèi)部能強(qiáng)引用捕獲的對象,因此當(dāng)該 Block 被對象強(qiáng)引用的時(shí)候就是注意以下的引用循環(huán)問題了:


引用循環(huán)

ARC 環(huán)境下解決方案

  1. 弱引用持有:使用__weak__unsafe_unretained捕獲對象解決

    弱引用持有

    • weak修飾的指針變量幽崩,在指向的內(nèi)存地址銷毀后苦始,會(huì)在 Runtime 的機(jī)制下,自動(dòng)置為nil慌申。
    • _unsafe_unretained不會(huì)置為nil陌选,容易出現(xiàn)懸垂指針,發(fā)生崩潰蹄溉。但是_unsafe_unretained__weak效率高咨油。
  2. 使用__block變量:使用__block修飾對象,在 block 內(nèi)部用完該對象后柒爵,將__block變量置為nil即可役电。雖然能控制對象的持有期間,并且能將其他對象賦值在__block變量中棉胀,但是必須執(zhí)行該 block法瑟。(意味著這個(gè)對象的生命周期完全歸我們控制)

    使用`__block`變量

MRC 環(huán)境下解決方案

  1. 弱引用持有:使用__unsafe_unretained捕獲對象
  2. 直接使用__block修飾對象,無需手動(dòng)將對象置為nil唁奢,因?yàn)榈讓?code>_Block_object_assign函數(shù)在 MRC 環(huán)境下對 block 內(nèi)部的對象不會(huì)進(jìn)行retain操作霎挟。

MRC 下的 Block

ARC 無效時(shí),需要手動(dòng)將 Block 從棧復(fù)制到堆麻掸,也需要手動(dòng)釋放 Block

  • 對于棧上的 Block 調(diào)用retain實(shí)例方法是不起作用的
  • 對于棧上的 Block 需要調(diào)用一次copy實(shí)例方式(引用計(jì)數(shù)+1)酥夭,將其配置在堆上,才可繼續(xù)使用retain實(shí)例方法
  • 需要減少引用的時(shí)候,只需調(diào)用release實(shí)例方法即可熬北。
  • 對于在 C 語言中使用 Block疙描,需要使用Block_copyBlock_release代替copyrelease
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末讶隐,一起剝皮案震驚了整個(gè)濱河市起胰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌整份,老刑警劉巖待错,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件籽孙,死亡現(xiàn)場離奇詭異烈评,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)犯建,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門讲冠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人适瓦,你說我怎么就攤上這事竿开。” “怎么了玻熙?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵否彩,是天一觀的道長。 經(jīng)常有香客問我嗦随,道長列荔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任枚尼,我火速辦了婚禮贴浙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘署恍。我一直安慰自己崎溃,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布盯质。 她就那樣靜靜地躺著袁串,像睡著了一般。 火紅的嫁衣襯著肌膚如雪呼巷。 梳的紋絲不亂的頭發(fā)上囱修,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機(jī)與錄音朵逝,去河邊找鬼蔚袍。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的啤咽。 我是一名探鬼主播晋辆,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼宇整!你這毒婦竟也來了瓶佳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤鳞青,失蹤者是張志新(化名)和其女友劉穎霸饲,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體臂拓,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡厚脉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了胶惰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片傻工。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖孵滞,靈堂內(nèi)的尸體忽然破棺而出中捆,到底是詐尸還是另有隱情,我是刑警寧澤坊饶,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布泄伪,位于F島的核電站,受9級特大地震影響匿级,放射性物質(zhì)發(fā)生泄漏蟋滴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一根蟹、第九天 我趴在偏房一處隱蔽的房頂上張望脓杉。 院中可真熱鬧,春花似錦简逮、人聲如沸球散。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蕉堰。三九已至,卻和暖如春悲龟,著一層夾襖步出監(jiān)牢的瞬間屋讶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工须教, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留皿渗,地道東北人斩芭。 一個(gè)月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像乐疆,于是被迫代替她去往敵國和親划乖。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345

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