iOS類(lèi)加載流程(一):類(lèi)加載流程的觸發(fā)

首先拙毫,大家應(yīng)該都知道 _objc_init 函數(shù)是 OC 中類(lèi)加載比較關(guān)鍵的一個(gè)函數(shù)押逼,這個(gè)函數(shù)的調(diào)用棧如下:

objc_init

那么箫踩,objc_init 這個(gè)函數(shù)是如何被調(diào)用的呢哎迄?又和 OC 中的類(lèi)加載有什么關(guān)系?類(lèi)又是如何被加載并以什么形式存在于運(yùn)行時(shí)呢蒂破?OC 中的成員變量馏谨、方法别渔、協(xié)議附迷、分類(lèi),這些都是如何實(shí)現(xiàn)的哎媚?

1. objc_init 的調(diào)用流程

從調(diào)用椑可以看到,_objc_init 起始于 doModinitFunctions 這個(gè)方法拨与。這個(gè)方法在 dyld 中稻据,因?yàn)?dyld3 都已經(jīng)在 iOS12 被全面使用了,dyld-433 仍然是 dyld2 的版本买喧,dyld-655 已經(jīng)是 dyld3 的版本了捻悯,所以這里以 dyld-655 的源碼來(lái)探索 _objc_init 的調(diào)用流程。

首先淤毛,doModinitFunctions 這個(gè)函數(shù)屬于 dyld 流程的“初始化方法調(diào)用”階段今缚。這一階段是整個(gè)流程的倒數(shù)第二步,也就是執(zhí)行 main 函數(shù)之前的階段低淡。

dyld 詳細(xì)流程見(jiàn)dyld:?jiǎn)?dòng)流程解析姓言。

doModinitFunctions 函數(shù)在 ImageLoader::recursiveInitialization 中被調(diào)用瞬项,關(guān)鍵代碼如下:

recursiveInitialization

先看看 doInitialization 方法的邏輯:

bool ImageLoaderMachO::doInitialization(const LinkContext& context) {
    CRSetCrashLogMessage2(this->getPath());

    // mach-o has -init and static initializers
    doImageInit(context);
    doModInitFunctions(context);
    
    CRSetCrashLogMessage2(NULL);
    
    return (fHasDashInit || fHasInitializers);
}

很明顯,關(guān)鍵邏輯在于 doImageInitdoModInitFunctions 這兩個(gè)函數(shù)何荚。

doImageInit 內(nèi)部經(jīng)過(guò)邏輯主要是找出該 Image 對(duì)應(yīng)的 mach-O 文件中 LC_ROUTINES 表內(nèi)的函數(shù)進(jìn)行調(diào)用:

doImageInit

LC_ROUTINES 的定義可以直接在 mach-o 庫(kù)的 loader.h 中看到囱淋,如果 dyld 源碼中無(wú)法跳轉(zhuǎn),可以在自己的項(xiàng)目中 import <mach-o/loader.h> 來(lái)看到具體的內(nèi)容:

/*
 * The routines command contains the address of the dynamic shared library 
 * initialization routine and an index into the module table for the module
 * that defines the routine.  Before any modules are used from the library the
 * dynamic linker fully binds the module that defines the initialization routine
 * and then calls it.  This gets called before any module initialization
 * routines (used for C++ static constructors) in the library.
 */
struct routines_command { /* for 32-bit architectures */
    uint32_t    cmd;        /* LC_ROUTINES */
    uint32_t    cmdsize;    /* total size of this command */
    uint32_t    init_address;   /* address of initialization routine */
    uint32_t    init_module;    /* index into the module table that */
                        /*  the init routine is defined in */
    uint32_t    reserved1;
    uint32_t    reserved2;
    uint32_t    reserved3;
    uint32_t    reserved4;
    uint32_t    reserved5;
    uint32_t    reserved6;
};

根據(jù)注釋來(lái)看餐塘,LC_ROUTINES 大概就是動(dòng)態(tài)庫(kù)在調(diào)用初始化函數(shù)之前需要被調(diào)用的函數(shù)妥衣。找了幾個(gè)動(dòng)態(tài)庫(kù),也沒(méi)有找到包含 LC_ROUTINES 這個(gè) load command 的動(dòng)態(tài)庫(kù)戒傻,暫時(shí)不深究吧~~~

緊接著称鳞,就來(lái)到了調(diào)用棧上最初的 doModInitFunctions 函數(shù)了,這個(gè)函數(shù)做了這么幾件事:

  1. 遞歸尋找 Load Command稠鼻,找到 S_MOD_INIT_FUNC_POINTERS 這個(gè) section 對(duì)應(yīng)的 Load Command冈止;
  2. 根據(jù) slide 計(jì)算 S_MOD_INIT_FUNC_POINTERS 的具體位置,并且取出這個(gè)表中的函數(shù)指針候齿;
  3. 進(jìn)行一系列判斷之后調(diào)用這些函數(shù)熙暴;
  4. 在函數(shù)調(diào)用前后進(jìn)行判斷,如果函數(shù)調(diào)用使得 dyld::gLibSystemHelpers 有值了慌盯,證明 libSystem 初始化完成周霉,此時(shí)將 dyld::gProcessInfo->libSystemInitialized 標(biāo)志置為 true;

關(guān)鍵代碼:

doModInitFunctions

簡(jiǎn)而言之:

  1. dyld 在動(dòng)態(tài)鏈接完成之后會(huì)執(zhí)行所有動(dòng)態(tài)庫(kù)的初始化函數(shù)亚皂,最后執(zhí)行主工程的初始化函數(shù)俱箱;
  2. 初始化函數(shù)需要使用 __attribute__修飾,編譯器識(shí)別之后會(huì)存儲(chǔ)在 Mach-O 文件的 __mod_init_func 中灭必;
  3. 因?yàn)?libSystem 是一系列系統(tǒng)庫(kù)的集合狞谱,被很多動(dòng)態(tài)庫(kù)依賴,優(yōu)先級(jí)更高禁漓,libSystem 的初始化函數(shù)會(huì)在比較靠前的順序開(kāi)始執(zhí)行(不是第一)跟衅。而 objc 就被包含在這個(gè)庫(kù)中。objc 庫(kù)的初始化方法 objc_init 就是在 libSystem 的初始化函數(shù)中被調(diào)用播歼;
  4. objc_init 方法中包含了 OC 類(lèi)的加載邏輯伶跷;

至此,可以做個(gè)階段性總結(jié)了:

  1. dyld 初始化函數(shù)調(diào)用階段會(huì)去遞歸調(diào)用 image 的初始化函數(shù)秘狞;
  2. libSystem 庫(kù)在比較靠前的位置被調(diào)用叭莫,進(jìn)而觸發(fā)了 _objc_init 函數(shù)的調(diào)用;

2. _objc_init 方法做了什么

來(lái)看下 objc_init 方法里面的代碼吧:

void _objc_init(void) {
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    // 環(huán)境初始化相關(guān)
    environ_init();
    // 線程相關(guān)
    tls_init();
    // objc庫(kù)初始化方法調(diào)用烁试,即objc庫(kù)中被__attribute__修飾的方法
    static_init();
    // 暫無(wú)任何邏輯
    lock_init();
    // NSSetUncaughtExceptionHandler()的基礎(chǔ)
    exception_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}

如上雇初,兩個(gè)點(diǎn)可以稍微關(guān)注下:

  1. static_init(); 方法調(diào)用了 objc 庫(kù)內(nèi)部的初始化方法。一般而言 image 的初始化方法在 dyld 的第八步中被調(diào)用廓潜,而 objc 則主動(dòng)調(diào)用了自己的初始化函數(shù)抵皱,有興趣的可以見(jiàn)后文善榛;
  2. exception_init(); 方法內(nèi)部實(shí)現(xiàn)是 iOS 中使用 NSSetUncaughtExceptionHandler() 的基礎(chǔ)。該方法可以設(shè)置 crash 后的處理邏輯呻畸,也是早起友盟移盆、bugly 等三方 crash 監(jiān)控 SDK 獲取 crash 堆棧信息的基礎(chǔ):
NSSetUncaughtExceptionHandler

接著看代碼,從 objc_init() 代碼來(lái)看伤为,貌似 objc 并沒(méi)有進(jìn)行類(lèi)的加載咒循?此時(shí)就需要關(guān)注 _dyld_objc_notify_register 以及對(duì)應(yīng)的三個(gè)回調(diào)了,這個(gè)方法是怎么個(gè)邏輯绞愚?

3. dyld 和 objc 的聯(lián)系

_dyld_objc_notify_register 這個(gè)方法由 dyld 提供叙甸,定義如下:

//
// Note: only for use by objc runtime
// Register handlers to be called when objc images are mapped, unmapped, and initialized.
// Dyld will call back the "mapped" function with an array of images that contain an objc-image-info section.
// Those images that are dylibs will have the ref-counts automatically bumped, so objc will no longer need to
// call dlopen() on them to keep them from being unloaded.  During the call to _dyld_objc_notify_register(),
// dyld will call the "mapped" function with already loaded objc images.  During any later dlopen() call,
// dyld will also call the "mapped" function.  Dyld will call the "init" function when dyld would be called
// initializers in that image.  This is when objc calls any +load methods in that image.
//
void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                _dyld_objc_notify_init      init,
                                _dyld_objc_notify_unmapped  unmapped);

注釋的大意是:

  1. 該方法專(zhuān)門(mén)為 objc-runtime 提供;
  2. 三個(gè)回調(diào)會(huì)在 image 被 mapped位衩、unmapped裆蒸、initialized 時(shí)分別被觸發(fā);
  3. dyld 在調(diào)用 mapped 這個(gè)回調(diào)時(shí)糖驴,會(huì)傳遞一個(gè) image 數(shù)組僚祷,這些 image 都是 objc 相關(guān)的 image;
  4. objc 不需要再調(diào)用 dlopen() 方法來(lái)加載或者維持這些 image贮缕。后續(xù)有新的 image 被載入時(shí)辙谜,仍然會(huì)調(diào)用 mapped 相關(guān)的回調(diào);
  5. dyld 會(huì)在調(diào)用 image 初始化函數(shù)階段觸發(fā) init 回調(diào)感昼,而這個(gè)回調(diào)就是 objc 調(diào)用 +load 方法的時(shí)機(jī)装哆;

緊接著,一一驗(yàn)證上述的注釋定嗓。首先在 dyld 中找到這個(gè)函數(shù):

_dyld_objc_notify_register

_dyld_objc_notify_register 只是一個(gè)對(duì)外包裝接口蜕琴,關(guān)鍵方法在 registerObjCNotifiers

registerObjCNotifiers

根據(jù)注釋?zhuān)琩yld 會(huì)通過(guò) notifyBatchPartial 函數(shù)觸發(fā) mapped 回調(diào)。因?yàn)?mapped 的回調(diào)被綁定到了 sNotifyObjCMapped 這個(gè)指針蜕乡,所以我們看代碼時(shí)只需要關(guān)注 sNotifyObjCMapped 的調(diào)用邏輯即可奸绷,來(lái)看看這個(gè)函數(shù)的關(guān)鍵代碼

notifyBatchPartial

打個(gè)斷點(diǎn)來(lái)驗(yàn)證:

iOS15

咦梗夸?有點(diǎn)不一樣层玲?別慌,這個(gè)是用的 iOS15 的模擬器反症,很明顯辛块,dyld4 已經(jīng)都被用上了。用 iOS12 的 iPhone7 看看:

notifyBatchPartial

完美铅碍,結(jié)論被完美驗(yàn)證~~~

即:

map_images() 是 objc 中類(lèi)初始化的主要函數(shù)润绵。該函數(shù)在 _objc_init() 調(diào)用 dyld 進(jìn)行回調(diào)綁定時(shí)就會(huì)通過(guò) notifyBatchPartial 被觸發(fā),進(jìn)而 objc 會(huì)對(duì)當(dāng)前所有 objc 相關(guān)的 image 進(jìn)行類(lèi)的初始化操作胞谈。

4. +load 函數(shù)的調(diào)用邏輯

+load 函數(shù)調(diào)用棧:

load

感覺(jué)核心在 notifySingle 這個(gè)函數(shù)尘盼,首先回到初始化函數(shù)的調(diào)用邏輯上憨愉,在recursiveInitialization 函數(shù)中對(duì) notifySingle 調(diào)用如下:

recursiveInitialization

上圖可看出:

  1. 在初始化操作之前調(diào)用了一次 notify,根據(jù)注釋可以看出卿捎,應(yīng)該是即將初始化對(duì)應(yīng) image 的一個(gè)通知配紫;
  2. 初始化操作之后之后,發(fā)送了初始化完成的通知午阵;

這里的重點(diǎn)在第一次 notify 的 dyld_image_state_dependents_initialized躺孝,來(lái)看看 notifySingle 函數(shù)中的關(guān)鍵代碼:

if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && image->notifyObjC() ) {
    uint64_t t0 = mach_absolute_time();

    (*sNotifyObjCInit)(image->getRealPath(), image->machHeader());

    uint64_t t1 = mach_absolute_time();
    uint64_t t2 = mach_absolute_time();
    uint64_t timeInObjC = t1-t0;
    uint64_t emptyTime = (t2-t1)*100;
    if ( (timeInObjC > emptyTime) && (timingInfo != NULL) ) {
        timingInfo->addTime(image->getShortName(), timeInObjC);
    }
}

不看 time 相關(guān)的代碼,關(guān)鍵代碼邏輯就是:

  1. 判斷 sNotifyObjCInit 是否存在底桂;
  2. 存在則執(zhí)行 sNotifyObjCInit植袍,傳遞 image 的 path 和 mh_header 的地址;

那么 sNotifyObjCInit 是個(gè)啥籽懦?全局搜一下找到:

registerObjCNotifiers

registerObjCNotifiers 又是啥呢于个?繼續(xù)全局搜索:

_dyld_objc_notify_register

很明顯,又是 _dyld_objc_notify_register 函數(shù)暮顺,這個(gè)函數(shù)注冊(cè)了三個(gè)回調(diào)览濒,load_image 就是在 image 的初始化函數(shù)即將被調(diào)用之前會(huì)被觸發(fā)的回調(diào)。

其實(shí) fishhook 也是用到了該文件下的 Api拖云,只不過(guò)是 _dyld_register_func_for_add_image函數(shù)贷笛,該方法是添加 image 相關(guān)的回調(diào),大概邏輯有點(diǎn)類(lèi)似宙项,具體就不贅述了乏苦,詳見(jiàn):iOS逆向:fishhook原理分析

總結(jié)下邏輯:

  1. objc 在初始化函數(shù) _objc_init 調(diào)用 dyld 的 Api 設(shè)置了依賴庫(kù)被加載時(shí)的回調(diào)尤筐;
  2. 依賴庫(kù)即將被調(diào)用初始化方法時(shí)汇荐,通過(guò)通知觸發(fā)回調(diào);
  3. 回調(diào)執(zhí)行預(yù)先設(shè)置的函數(shù)盆繁,也就是 objc 中的 load_images 函數(shù)掀淘;
  4. load_images 函數(shù)執(zhí)行 objc 的類(lèi)加載的邏輯,觸發(fā) +load 方法的調(diào)用油昂;

另外革娄,需要關(guān)注一點(diǎn):notifySingle() 函數(shù)的觸發(fā)時(shí)在初始化函數(shù)調(diào)用之前,也就是說(shuō)冕碟,必須在所有依賴庫(kù)的初始化函數(shù)執(zhí)行 之前 (也就是兩個(gè)通知的前者)進(jìn)行 objc 的 +load 邏輯拦惋。

實(shí)現(xiàn)了 +load 方法的類(lèi)會(huì)被添加到費(fèi)懶加載類(lèi),在 map 的回調(diào)中就會(huì)調(diào)用 realizeClassWithoutSwift 進(jìn)行加載和初始化安寺。在 +load 調(diào)用之前厕妖,為了防止遺漏,仍然會(huì)進(jìn)行一次 realizeClassWithoutSwift 的調(diào)用挑庶。另外言秸,+load 方法最初的設(shè)計(jì)目的是什么软能?

5. initialize 方法調(diào)用流程

詳見(jiàn):徹底搞懂+load和+initialize

6. 補(bǔ)充:objc 自己調(diào)用初始化函數(shù)

比較好玩的一點(diǎn)是:objc 庫(kù)中的初始化函數(shù)是 objc 自己調(diào)用的,而不是 dyld举畸。

這里首先要從我們經(jīng)常涉及到的 objc_init 來(lái)說(shuō)起:

objc_init

該方法通過(guò) static_init 方法完成了 objc 庫(kù)中初始化方法的調(diào)用:

static_init

看看 getLibobjcInitializers 是個(gè)啥埋嵌?

mach-O

本質(zhì)上是常見(jiàn)的 GETSECT 方法,但是這里最關(guān)鍵的是 __objc_init_func 俱恶。objc 用這個(gè)標(biāo)記來(lái)在 mach-O 文件中來(lái)標(biāo)識(shí) objc 獨(dú)有的初始化函數(shù)雹嗦。

但是,初始化方法不是一般都存放在 __mod_init 這個(gè) section 中嗎合是? dyld 內(nèi)部也是這個(gè)邏輯:

dyld初始化函數(shù)調(diào)用

這個(gè) S_MOD_INIT_FUNC_POINTERS 在 mach-o 相關(guān)的源碼中:

image.png

實(shí)際測(cè)試結(jié)果:

初始化函數(shù)

結(jié)論:dyld 通過(guò) mach-O 文件中的 __mod_init 這個(gè) section 來(lái)記錄并調(diào)用初始化函數(shù)了罪;

看到這里會(huì)想當(dāng)疑惑,難道 objc 的初始化方法不是 dyld 加載的聪全?繼續(xù)查找 objc 源碼泊藕,看看 objc 對(duì)這個(gè) __objc_init_func 做了什么? 在 markgc 的 main 函數(shù)中做了這么一個(gè)操作:

imarkgc

markgc 是個(gè)啥难礼?猜測(cè)是個(gè)腳本之類(lèi)的東西娃圆?markgc 的 main 函數(shù)最終會(huì)觸發(fā)這個(gè) dosect 方法。也就是說(shuō) markgc 這個(gè)程序在 objc 的 mach-O 文件生成之后(可以理解成被編譯成了動(dòng)態(tài)庫(kù)之后)蛾茉,手動(dòng)修改了初始化方法對(duì)應(yīng)的 sectionName 和 type讼呢。

而 dyld 調(diào)用初始化方法是通過(guò) mach-O 文件中的 __mod_init 這個(gè) section 來(lái)完成調(diào)用的。objc 做了這么個(gè)騷操作之后谦炬, dyld 就不會(huì)(沒(méi)辦法悦屏,因?yàn)檎也坏綄?duì)應(yīng)的 section)在初始化階段(倒數(shù)第二步,即調(diào)用 main 函數(shù)之前)去調(diào)用這些初始化函數(shù)了键思。

按照 Apple 給出的理由是础爬,dyld 調(diào)用 objc 的初始化函數(shù)的時(shí)機(jī)太晚,主要是晚于 libc 的調(diào)用:

libc calls _objc_init() before dyld would call our static constructors, so we have to do it ourselves

總結(jié):

  1. libc 可能被包裝在了 libSystem 中吼鳞,而 libc 需要調(diào)用 objc看蚜,且這個(gè)調(diào)用發(fā)生在 dyld 調(diào)用 objc 初始化函數(shù)之前,所以 objc 需要自己來(lái)調(diào)用初始化函數(shù)赔桌;
  2. objc 通過(guò) markgc 程序?qū)?__mod_init 修改為 __objc_init_function供炎,從而適配自己的 static_init 邏輯,同時(shí)也避免了 dyld 對(duì) objc 初始化函數(shù)的重復(fù)調(diào)用纬乍;

7. 一個(gè)疑問(wèn)

image list

如上圖碱茁,斷點(diǎn)打在 ImageLoaderMachO::doModInitFunctions 時(shí),libSystem 顯然不是第一個(gè) image仿贬,此時(shí)就有個(gè)疑問(wèn):

  1. 為什么自己嵌入的動(dòng)態(tài)庫(kù) UPBase 那么靠前?
  2. 如果按照這個(gè) image list 順序進(jìn)行初始化調(diào)用墓贿,那么 UPBase 被初始化時(shí)肯定 libSystem 還沒(méi)有初始化茧泪。雖然 map_images() 在后續(xù)被調(diào)用時(shí)會(huì)遍歷所有 image蜓氨,但是如果是涉及到 UPBase 中有初始化函數(shù)調(diào)用,那么此時(shí) objc 仍然沒(méi)有初始化的队伟,這樣會(huì)不會(huì)有問(wèn)題穴吹?
  3. 如果沒(méi)問(wèn)題,那邏輯是怎樣的呢嗜侮? dyld 是進(jìn)行了 image 順序調(diào)整港令,類(lèi)似于依賴層級(jí)調(diào)整?或者說(shuō) image list 指令打印的不是當(dāng)前 dyld 中的 image list 中的順序锈颗?

解釋?zhuān)褐鞴こ?image 雖然在第一個(gè)顷霹,但是給到 objc 的 image 數(shù)組進(jìn)行了順序調(diào)整,且給過(guò)去之后 objc 是逆序讀取 image击吱,所以 objc 相關(guān)的 libSystem 庫(kù)比較靠前淋淀,優(yōu)先加載。

可以通過(guò)在 map_image_nolock 方法中打斷點(diǎn)覆醇,然后查看寄存區(qū)朵纷,獲取入?yún)ⅲㄟ^(guò)打印入?yún)⒌姆绞讲榭?image list 的排序:

map_image_nolock
逆序讀取image

因?yàn)樽址嵌鄠€(gè)字符加上 \0 組成永脓,所以字符串本身就是一個(gè)數(shù)組袍辞,使用 char *表示。而 path 是字符串?dāng)?shù)組常摧,所以指向一個(gè)字符串?dāng)?shù)組革屠,需要使用 char ** 表示:

image list

如上圖可以看到,因?yàn)榇蛴?path[306] 出了異常排宰,所以數(shù)組總個(gè)數(shù)是 306似芝。另外,可以直接打印 x0 寄存器就可以知道 imageCount板甘,只不過(guò)上圖沒(méi)有空間顯示了党瓮。

上圖可以看到,逆序之后 libSystem 并不是處于第一個(gè)位置盐类,所以庫(kù)的優(yōu)先級(jí)和初始化順序大概有幾種:

  1. 優(yōu)先級(jí)大于 libSystem 的系統(tǒng)庫(kù)寞奸;
  2. libSystem;
  3. 系統(tǒng)庫(kù)在跳,如 libobjc 等枪萄;
  4. 工程中插入的動(dòng)態(tài)庫(kù);
  5. 主工程猫妙;

8. 總結(jié)

一張圖做個(gè)總結(jié)吧:

總結(jié)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瓷翻,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌齐帚,老刑警劉巖妒牙,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異对妄,居然都是意外死亡湘今,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)剪菱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)摩瞎,“玉大人,你說(shuō)我怎么就攤上這事孝常∑烀牵” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵茫因,是天一觀的道長(zhǎng)蚪拦。 經(jīng)常有香客問(wèn)我,道長(zhǎng)冻押,這世上最難降的妖魔是什么驰贷? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮洛巢,結(jié)果婚禮上括袒,老公的妹妹穿的比我還像新娘。我一直安慰自己稿茉,他們只是感情好锹锰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著漓库,像睡著了一般恃慧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上渺蒿,一...
    開(kāi)封第一講書(shū)人閱讀 51,182評(píng)論 1 299
  • 那天痢士,我揣著相機(jī)與錄音,去河邊找鬼茂装。 笑死怠蹂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的少态。 我是一名探鬼主播城侧,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼彼妻!你這毒婦竟也來(lái)了嫌佑?” 一聲冷哼從身側(cè)響起豆茫,我...
    開(kāi)封第一講書(shū)人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎歧强,沒(méi)想到半個(gè)月后澜薄,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體为肮,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡摊册,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了颊艳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茅特。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖棋枕,靈堂內(nèi)的尸體忽然破棺而出白修,到底是詐尸還是另有隱情,我是刑警寧澤重斑,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布兵睛,位于F島的核電站,受9級(jí)特大地震影響窥浪,放射性物質(zhì)發(fā)生泄漏祖很。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一漾脂、第九天 我趴在偏房一處隱蔽的房頂上張望假颇。 院中可真熱鬧,春花似錦骨稿、人聲如沸笨鸡。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)形耗。三九已至,卻和暖如春辙浑,著一層夾襖步出監(jiān)牢的瞬間激涤,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工例衍, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留昔期,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓佛玄,卻偏偏與公主長(zhǎng)得像硼一,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子梦抢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

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