iOS底層原理總結(jié) -- 利用Runtime源碼 分析Category的底層實(shí)現(xiàn)

iOS底層原理總結(jié) -- 利用Runtime源碼 分析Category的底層實(shí)現(xiàn)

窺探iOS底層實(shí)現(xiàn)--OC對(duì)象的本質(zhì)(一)

窺探iOS底層實(shí)現(xiàn)--OC對(duì)象的本質(zhì)(二)

窺探iOS底層實(shí)現(xiàn)--OC對(duì)象的分類:instance、class果元、meta-calss對(duì)象的isa和superclass

窺探iOS底層實(shí)現(xiàn)-- KVO/KVC的本質(zhì)

iOS底層原理總結(jié) -- 利用Runtime源碼 分析Category的底層實(shí)現(xiàn)

...

前言:

本文總結(jié)了一下Category中的內(nèi)部去實(shí)現(xiàn)部分闷煤,代碼部分較多谢床,添加了注釋,閱讀起來可能比較枯燥谈截。但是請(qǐng)大家務(wù)必堅(jiān)持讀完屯伞。會(huì)有更多的收貨,

思考:

  1. Category的實(shí)現(xiàn)原理浙巫?

  2. 為什么Category的中的方法會(huì)優(yōu)先調(diào)用?

  3. 延伸問題 - 如果多個(gè)分類中都實(shí)現(xiàn)了同一個(gè)方法刷后,那么在調(diào)用該方法的時(shí)候會(huì)優(yōu)先調(diào)用哪一個(gè)方法的畴?

  4. 擴(kuò)展和分類的區(qū)別?

Category 基本實(shí)現(xiàn)

首先 看一下分類代碼代碼的實(shí)現(xiàn) 可選擇性跳過

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="objective-c" cid="n41" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; background-color: rgb(51, 51, 51); font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">///> main.h
int main(int argc, const char *argv[]){
@autoreleasepool{
Person *person = [[Person alloc] init]
[person run];
[person test];
[person eat];
}
return 0
}
?
///> person
@interface Person: NSObject
@end
@implementation Person

  • (void)run{
    Nslog(@"run")
    }
    @end
    ?
    ///> person+test
    @interface Person(test)
  • (void)test;
    @end
    @implementation Person(test)
  • (void)test{
    Nslog(@"test")
    }
    @end

///> person+Eat
@interface Person(eat)

  • (void)eat;
    @end
    @implementation Person(eat)
  • (void)eat{
    Nslog(@"eat")
    }
    @end</pre>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="shell" cid="n109" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; background-color: rgb(51, 51, 51); font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">xcrun -sdk iphoneos clang -arch arm64 - OC源文件 -o 輸出的CPP文件</pre>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="objective-c" cid="n45" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; background-color: rgb(51, 51, 51); font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">struct _category_t{
const char *name; ///> 分類的名字
struct _class_t *cls; ///> class
const struct _method_list_t *instance_methods; ///> 實(shí)例方法列表
const struct _method_list_t *class_methods; ///> 類方法列表
const struct _protocol_list_t *protocols; ///> 協(xié)議
const struct _prop_list_t *properties; ///> 屬性
}</pre>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="c++" cid="n117" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; background-color: rgb(51, 51, 51); font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">static struct category_t OBJCCATEGORY_PERSON__Test attribute ((userd, section("__DATA,__objc_const")))={
///> 屬于那個(gè)類的分類
"Person",
///> class
0,
///> 對(duì)象方法列表
(const struct _method_list_t *)&OBJC_CATEGORY_INSTANCE_METHODS_Person_Test,
///> 類方法列表
(const struct _method_list_t *)&OBJC_CATEGORY_CLASS_METHODS_Person_Test,
///> 協(xié)議列表
0, // (const _protocol_list_t *)&OBJC_CATEGORY_PROTOCOLS_PERSON__Test,
///> 屬性列表
0, // (const _prop_list_t *)&OBJC_PROP_LIST_PERSON__Test,
}</pre>

  1. 搜索 "catrgory_t {"

    <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="c++" cid="n78" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; background-color: rgb(51, 51, 51); font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">struct category_t {
    const char *name;
    classref_t cls;
    struct method_list_t *instanceMethods;
    struct method_list_t *classMethods;
    struct protocol_list_t *protocols;
    struct property_list_t *instanceProperties;
    // Fields below this point are not always present on disk.
    struct property_list_t *_classProperties;
    ?
    method_list_t *methodsForMeta(bool isMeta) {
    if (isMeta) return classMethods;
    else return instanceMethods;
    }
    ?
    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
    };</pre>

    可以看到 Runtime中的結(jié)構(gòu)和上面的category_t的結(jié)構(gòu)類似惠险。

  2. Runtime的程序入口文件為objc-os.mm 文件苗傅,

  3. 我這里直接到 有關(guān)Category的代碼部分 在objc-runtime-new.mm文件中 搜搜Discover categories. 的注釋代碼

    <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="c++" cid="n130" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; background-color: rgb(51, 51, 51); font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;"> // Discover categories.
    for (EACH_HEADER) {
    /**
    catlist 是一個(gè)二維數(shù)組,
    每一個(gè)分類都會(huì)創(chuàng)建一個(gè)category_t的結(jié)構(gòu)體
    這里的二維數(shù)組放了兩個(gè)分類結(jié)構(gòu)體的內(nèi)容 如代碼中的 eat和test
    catlist = [[],[]]
    */
    category_t **catlist =
    _getObjc2CategoryList(hi, &count);
    bool hasClassProperties = hi->info()->hasCategoryClassProperties();
    ?
    ///> 將每一個(gè)數(shù)組中的內(nèi)容遍歷
    for (i = 0; i < count; i++) {
    ///> 獲取 單獨(dú)的category_t結(jié)構(gòu)體
    category_t *cat = catlist[i];
    ///> 重新映射class 取出結(jié)構(gòu)體的class
    Class cls = remapClass(cat->cls);
    ?
    if (!cls) {
    // Category's target class is missing (probably weak-linked).
    // Disavow any knowledge of this category.
    catlist[i] = nil;
    if (PrintConnecting) {
    _objc_inform("CLASS: IGNORING category ???(%s) %p with "
    "missing weak-linked target class",
    cat->name, cat);
    }
    continue;
    }
    ?
    // Process this category.
    // First, register the category with its target class.
    // Then, rebuild the class's method lists (etc) if
    // the class is realized.
    bool classExists = NO;
    /// 判斷結(jié)構(gòu)體的內(nèi)容
    if (cat->instanceMethods || cat->protocols
    || cat->instanceProperties)
    {
    addUnattachedCategoryForClass(cat, cls, hi);
    if (cls->isRealized()) {
    /// 核心內(nèi)容 : 重新組織類中的方法
    remethodizeClass(cls);
    classExists = YES;
    }
    if (PrintConnecting) {
    _objc_inform("CLASS: found category -%s(%s) %s",
    cls->nameForLogging(), cat->name,
    classExists ? "on existing class" : "");
    }
    }
    ?
    if (cat->classMethods || cat->protocols
    || (hasClassProperties && cat->_classProperties))
    {
    addUnattachedCategoryForClass(cat, cls->ISA(), hi);
    if (cls->ISA()->isRealized()) {
    /// 核心內(nèi)容 : 重新組織類中的元類方法
    remethodizeClass(cls->ISA());
    }
    if (PrintConnecting) {
    _objc_inform("CLASS: found category +%s(%s)",
    cls->nameForLogging(), cat->name);
    }
    }
    }
    }</pre>

    以上代碼中找到了 核心的方法: remethodizeClass 使用了兩次 班巩, 重新組織類的方法和元類的方法

  4. command+單機(jī)渣慕,進(jìn)入

    <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="c++" cid="n144" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; background-color: rgb(51, 51, 51); font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">static void remethodizeClass(Class cls)
    {
    category_list cats;
    bool isMeta;
    runtimeLock.assertWriting();
    isMeta = cls->isMetaClass();
    // Re-methodizing: check for more categories
    if ((cats = unattachedCategoriesForClass(cls, false/
    not realizing*/))) {
    if (PrintConnecting) {
    _objc_inform("CLASS: attaching categories to class '%s' %s",
    cls->nameForLogging(), isMeta ? "(meta)" : "");
    }

    ///> 附加分類的代碼調(diào)用 , 傳入了 類對(duì)象抱慌、分類逊桦。
    ///> cls: [Person class]
    ///> cats: [category_t(test), category_t(eat)]
    attachCategories(cls, cats, true /flush caches/);
    free(cats);
    }
    }</pre>

  5. command 進(jìn)入 attachCategories(cls, cats, true /flush caches/); 方法

    <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="c++" cid="n157" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; background-color: rgb(51, 51, 51); font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">///> cls: [Person class]
    ///> cats: [category_t(test), category_t(eat)]
    static void
    attachCategories(Class cls, category_list *cats, bool flush_caches){
    if (!cats) return;
    if (PrintReplacedMethods) printReplacements(cls, cats);
    ?
    ///> 是否是元類對(duì)象
    bool isMeta = cls->isMetaClass();
    ?
    // fixme rearrange to remove these intermediate allocations
    ///> malloc 分配內(nèi)存
    ///> 方法數(shù)組 二維數(shù)組 eg:[[method_t,method_t], [method_t,method_t]]
    method_list_t **mlists = (method_list_t *)
    malloc(cats->count * sizeof(
    mlists));

    ///> 屬性數(shù)組 eg:[[property_t,property_t], [property_t,property_t]]
    property_list_t **proplists = (property_list_t *)
    malloc(cats->count * sizeof(
    proplists));

    ///> 協(xié)議數(shù)組 eg:[[protocol_t,protocol_t], [protocol_t,protocol_t]]
    protocol_list_t **protolists = (protocol_list_t *)
    malloc(cats->count * sizeof(
    protolists));
    ?
    // Count backwards through cats to get newest categories first
    int mcount = 0;
    int propcount = 0;
    int protocount = 0;
    int i = cats->count;
    bool fromBundle = NO;
    while (i--) {
    ///> 取出某個(gè)分類
    auto& entry = cats->list[i];
    ///> 取出分類中的對(duì)象方法
    method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
    ///> 將每一個(gè)分類的方法列表數(shù)組放在 上方定義的二維數(shù)組當(dāng)中!
    if (mlist) {
    mlists[mcount++] = mlist;
    fromBundle |= entry.hi->isBundle();
    }
    ?
    property_list_t proplist =
    entry.cat->propertiesForMeta(isMeta, entry.hi);
    ///> 將每一個(gè)分類的協(xié)議列表數(shù)組放在 上方定義的二維數(shù)組當(dāng)中抑进!
    if (proplist) {
    proplists[propcount++] = proplist;
    }
    ?
    protocol_list_t protolist = entry.cat->protocols;
    ///> 將每一個(gè)分類的屬性列表數(shù)組放在 上方定義的二維數(shù)組當(dāng)中强经!
    if (protolist) {
    protolists[protocount++] = protolist;
    }
    }
    ?
    ///> 取出類對(duì)象中的數(shù)據(jù)
    auto rw = cls->data();
    ?
    prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
    /

    核心代碼:
    rw: 類對(duì)象結(jié)構(gòu)體中 有一個(gè)erw的結(jié)構(gòu),
    這一步驟就是將數(shù)據(jù)合并到類對(duì)象的 rw結(jié)構(gòu)中去 請(qǐng)參照文章:
    將所有的分類的對(duì)象方法 附加到類對(duì)象中去寺渗!
    也就是 在運(yùn)行d時(shí)的時(shí)候講 分類的數(shù)據(jù)合并到了原始的類對(duì)象中D淝椤!
    */
    rw->methods.attachLists(mlists, mcount);

    free(mlists);
    if (flush_caches && mcount > 0) flushCaches(cls);
    ?
    ///> 同理屬性方法列表
    rw->properties.attachLists(proplists, propcount);
    free(proplists);

    ///> 同理協(xié)議方法列表
    rw->protocols.attachLists(protolists, protocount);
    free(protolists);
    }</pre>

    如有錯(cuò)誤之處還請(qǐng)各位大神指出P攀狻炬称!


    再次感謝!涡拘!

    1. Runtime源碼地址:Source Browser:OBJective-c源碼找到objc4玲躯,下載版本號(hào)最大是最新的源碼

    2. MJ老師底層相關(guān)視頻

    參考:


    1. 擴(kuò)展和分類的區(qū)別

      擴(kuò)展@interface 是匿名分類, 不是分類。 就是屬性添加 在編譯的時(shí)候就加入到了類中

      category在runtime中才合并的跷车。

    2. Category的實(shí)現(xiàn)原理棘利?

      原理:底層結(jié)構(gòu)是結(jié)構(gòu)體 categoty_t 創(chuàng)建好分類之后分兩個(gè)階段:

      1. 編譯階段:

        將每一個(gè)分類都生成所對(duì)應(yīng)的 category_t結(jié)構(gòu)體, 結(jié)構(gòu)體中存放 分類的所屬類name朽缴、class善玫、對(duì)象方法列表、類方法列表不铆、協(xié)議列表蝌焚、屬性列表。

      2. Runtime運(yùn)行時(shí)階段:

        將生成的分類數(shù)據(jù)合并到原始的類中去誓斥,某個(gè)類的分類數(shù)據(jù)會(huì)在合并到一個(gè)大的數(shù)組當(dāng)中(后參與編譯的分類會(huì)在數(shù)組的前面),分類的方法列表许帐,屬性列表劳坑,協(xié)議列表等都放在二維數(shù)組當(dāng)中,然后重新組織類中的方法成畦,將每一個(gè)分類對(duì)應(yīng)的列表的合并到原始類的列表中距芬。(合并前會(huì)根據(jù)二維數(shù)組的數(shù)量擴(kuò)充原始類的列表,然后將分類的列表放入前面)

    3. 為什么Category的中的方法會(huì)優(yōu)先調(diào)用循帐?

      如上所述框仔, 在擴(kuò)充數(shù)組的時(shí)候 會(huì)將原始類中擁有的方法列表移動(dòng)到后面, 將分類的方法列表數(shù)據(jù)放在前面拄养,所以分類的數(shù)據(jù)會(huì)優(yōu)先調(diào)用

    4. 延伸問題 - 如果多個(gè)分類中都實(shí)現(xiàn)了同一個(gè)方法离斩,那么在調(diào)用該方法的時(shí)候會(huì)優(yōu)先調(diào)用哪一個(gè)方法?

      在多個(gè)分類中擁有相同的方法的時(shí)候瘪匿, 會(huì)根據(jù)編譯的先后順序 來添加分類方法列表跛梗, 后編譯的分類方法在最前面,所以要看 Build Phases --> compile Sources中的順序棋弥。 后參加編譯的在前面核偿。

    由源碼分析我們可以得知,

    總結(jié)分類的一些問題

    1. command 進(jìn)入 rw->methods.attachLists(mlists, mcount); 方法中

      <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="c++" cid="n165" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; background-color: rgb(51, 51, 51); font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;"> /**
      addedLists: [[method_t, method_t],[method_t, method_t]]
      addedCount: 2 s二維數(shù)組的數(shù)量
      /
      void attachLists(List
      const * addedLists, uint32_t addedCount) {
      if (addedCount == 0) return;
      ?
      if (hasArray()) {
      // many lists -> many lists
      ///> 原始數(shù)組中的大小 每添加這個(gè)分類的
      uint32_t oldCount = array()->count;
      ///> 新的數(shù)組大型缛尽: 原始的加上新傳入的 總計(jì)大小
      uint32_t newCount = oldCount + addedCount;
      ///> realloc 重新分配內(nèi)存 newCont
      ///> 為了合并分類中的數(shù)組 擴(kuò)充原來數(shù)組的大小
      ///> 需要重新分配內(nèi)存
      setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
      array()->count = newCount;

      /*
      內(nèi)存移動(dòng)
      array()->lists 原來的方法列表
      addedCount 分類的數(shù)組的count

      將原來的方法列表挪動(dòng)到新的位置漾岳,
      (array()->lists + addedCount addedCount是挪動(dòng)的位數(shù)
      相當(dāng)有將原來的方法放到了最后!
      */
      memmove(array()->lists + addedCount, array()->lists,
      oldCount * sizeof(array()->lists[0]));

      /*
      內(nèi)存挪動(dòng) 拷貝
      array()->lists 原來的方法列表
      addedLists 傳進(jìn)來的分類list

      上面的方法已經(jīng)將 類的方法列表做到了擴(kuò)充 并且類原始帶的方法列表向后挪動(dòng)的 addedCount的位數(shù)
      為的就是 將傳入的分類的方法列表 拷貝到array()->lists(原始方法列表)的最前面粉寞,

      所以 這就是分類的數(shù)據(jù)會(huì)優(yōu)先調(diào)用的 原因
      /
      memcpy(array()->lists, addedLists,
      addedCount * sizeof(array()->lists[0]));
      }
      else if (!list && addedCount == 1) {
      // 0 lists -> 1 list
      list = addedLists[0];
      }
      else {
      // 1 list -> many lists
      List
      oldList = list;
      uint32_t oldCount = oldList ? 1 : 0;
      uint32_t newCount = oldCount + addedCount;
      setArray((array_t *)malloc(array_t::byteSize(newCount)));
      array()->count = newCount;
      if (oldList) array()->lists[addedCount] = oldList;
      memcpy(array()->lists, addedLists,
      addedCount * sizeof(array()->lists[0]));
      }
      }</pre>

首先下載Runtimed的源碼尼荆。 ------ 這里用xcode打開

Runtime源碼分析

接下來查看一下 Runtime的源碼是怎么將分類合并的,

每創(chuàng)建一個(gè)類都會(huì) 根會(huì)根據(jù)如下方法創(chuàng)建一個(gè)category_t的結(jié)構(gòu)體

接下來直接搜索 category_t 得出如下結(jié)構(gòu)體 我已經(jīng)將注釋放在后面了

可以將其拖入到xcode中仁锯, 方便搜索

命令可以查看轉(zhuǎn)化為C\C++代碼耀找。會(huì)有生成一個(gè)xxx.cpp的文件就是我們想要的文件

利用:

分類代碼 C\C++源碼分析

下面是源碼觀看的過程在每一步都給出了注釋, 有點(diǎn)枯燥,但是看完之后會(huì)很受益野芒。

編譯完畢之后 category存放在 結(jié)構(gòu)體category_t中 并沒有合并到 原始類中 每一個(gè)分類都會(huì)生成catrgory_t的結(jié)構(gòu)體蓄愁, 在運(yùn)行時(shí)的時(shí)候才會(huì)將分類中的方法、協(xié)議狞悲、屬性等 合并到原始的類中去撮抓。

編譯完畢的時(shí)候 一開始程序運(yùn)行的時(shí)候 所有分類的方法 一開始都存放在 結(jié)構(gòu)體中(每一個(gè)分類都有一個(gè)新的結(jié)構(gòu)體對(duì)象),

分類的底層結(jié)構(gòu)體 編譯完畢之后

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末摇锋,一起剝皮案震驚了整個(gè)濱河市丹拯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌荸恕,老刑警劉巖乖酬,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異融求,居然都是意外死亡咬像,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門生宛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來县昂,“玉大人,你說我怎么就攤上這事陷舅〉拐茫” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵莱睁,是天一觀的道長(zhǎng)待讳。 經(jīng)常有香客問我,道長(zhǎng)缩赛,這世上最難降的妖魔是什么耙箍? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮酥馍,結(jié)果婚禮上辩昆,老公的妹妹穿的比我還像新娘。我一直安慰自己旨袒,他們只是感情好汁针,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著砚尽,像睡著了一般施无。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上必孤,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天猾骡,我揣著相機(jī)與錄音瑞躺,去河邊找鬼。 笑死兴想,一個(gè)胖子當(dāng)著我的面吹牛幢哨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嫂便,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼捞镰,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了毙替?” 一聲冷哼從身側(cè)響起岸售,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎厂画,沒想到半個(gè)月后凸丸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡袱院,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年甲雅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坑填。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖弛姜,靈堂內(nèi)的尸體忽然破棺而出脐瑰,到底是詐尸還是另有隱情,我是刑警寧澤廷臼,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布苍在,位于F島的核電站,受9級(jí)特大地震影響荠商,放射性物質(zhì)發(fā)生泄漏寂恬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一莱没、第九天 我趴在偏房一處隱蔽的房頂上張望初肉。 院中可真熱鬧,春花似錦饰躲、人聲如沸牙咏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)妄壶。三九已至,卻和暖如春寄狼,著一層夾襖步出監(jiān)牢的瞬間丁寄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留伊磺,地道東北人盛正。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像奢浑,于是被迫代替她去往敵國(guó)和親蛮艰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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