iOS底層原理18:類的加載

在上一篇我們分析了_objc_init方法,程序運行時肴焊,dyld將使用包含objc_image_info的鏡像文件數(shù)組前联,回調(diào) mapped 函數(shù),最后會執(zhí)行libObjcmap_images方法

map_images的主要流程

map_images方法

  • map_images方法源碼如下娶眷,從注釋可以看出蛀恩,這個方法主要用來處理映射到內(nèi)存中的鏡像文件
/***********************************************************************
* map_images
* Process the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: write-locks runtimeLock
**********************************************************************/
void
map_images(unsigned count, const char * const paths[],
           const struct mach_header * const mhdrs[])
{
    mutex_locker_t lock(runtimeLock);
    return map_images_nolock(count, paths, mhdrs);
}

map_images_nolock方法

  • 查看map_images_nolock方法,里面代碼比較多茂浮,直接看重點
image

_read_images方法

  • 查看_read_images方法
image

_read_images內(nèi)容比較多双谆,根據(jù)蘋果注釋信息,主要有以下幾步:

  • 條件控制進行一次加載
  • 修復(fù)預(yù)編譯階段的@selector的混亂的問題
  • 錯誤混亂的類處理
  • 修復(fù)重映射一些沒有被鏡像文件加載進來的類
  • 修復(fù)一些消息
  • 當類中有協(xié)議時:readProtocol
  • 修復(fù)沒有被加載的協(xié)議
  • 分類的處理
  • 類的加載處理
  • 沒有被處理的類席揽,優(yōu)化那些被侵犯的類

條件控制進行一次加載

doneOnce是全局靜態(tài)變量顽馋,加載一次后doneOnce=YES,下次就不會在進入判斷幌羞。第一次進來主要創(chuàng)建表gdb_objc_realized_classes寸谜,表里存放的是不在dyld共享緩存中的命名類,無論是否實現(xiàn)

static bool doneOnce;
if (!doneOnce) {
    doneOnce = YES; // doneOnce:全局靜態(tài)變量属桦,只加載一次
    launchTime = YES;
    
    // ...此處省略代碼
    // namedClasses 是不在dyld共享緩存中的命名類熊痴,無論是否實現(xiàn)。
    // Preoptimized classes don't go in this table.
    // 4/3 is NXMapTable's load factor 4/3是NXMapTable的負載系數(shù)
    int namedClassesSize = 
        (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
    // 創(chuàng)建哈希表 存放所有的類
    gdb_objc_realized_classes =
        NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);

    ts.log("IMAGE TIMES: first time tasks");
}

修復(fù)@selector的混亂

  • 修復(fù)@selector的混亂聂宾,從macho文件中獲取對象方法列表果善,方法列表存放在 DATA段__objc_selrefs
// Fix up @selector references
static size_t UnfixedSelectors;
{
    mutex_locker_t lock(selLock);
    for (EACH_HEADER) {
        if (hi->hasPreoptimizedSelectors()) continue;

        bool isBundle = hi->isBundle();
        // 從macho文件中獲取方法名列表,方法列表存放在 DATA段 的 __objc_selrefs
        SEL *sels = _getObjc2SelectorRefs(hi, &count);
        UnfixedSelectors += count;
        for (i = 0; i < count; i++) {
            const char *name = sel_cname(sels[i]);
            SEL sel = sel_registerNameNoLock(name, isBundle);
            if (sels[i] != sel) {
                // TODO:自定義打印
                _objc_inform("=======修復(fù)@selector的混亂:%p = %p", (SEL)sels[i], sel);
                sels[i] = sel;
            }
        }
    }
}
image

錯誤混亂的類處理

// Discover classes. Fix up unresolved future classes. Mark bundle classes.
// 發(fā)現(xiàn)類系谐。修復(fù)未解析的未來類巾陕。標記捆綁類
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();

for (EACH_HEADER) {
    if (! mustReadClasses(hi, hasDyldRoots)) {
        // Image is sufficiently optimized that we need not call readClass()
        continue;
    }
    
    // 從Mach-O中讀取類列表信息,類總數(shù)count
    classref_t const *classlist = _getObjc2ClassList(hi, &count);
    if (hIndex == hCount - 1) {
        _objc_inform("=======發(fā)現(xiàn)類纪他。修復(fù)未解析的未來類鄙煤。標記捆綁類。:HTTest = %zu", count);
    }
    bool headerIsBundle = hi->isBundle();
    bool headerIsPreoptimized = hi->hasPreoptimizedClasses();

    for (i = 0; i < count; i++) {
        Class cls = (Class)classlist[I];
        Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
        
        // 類信息發(fā)生混亂茶袒,類運行時可能發(fā)生移動梯刚,但是沒有被刪除,相當于常說的野指針
        if (newCls != cls  &&  newCls) {
            // Class was moved but not deleted. Currently this occurs 
            // only when the new class resolved a future class.
            // Non-lazily realize the class below.
            resolvedFutureClasses = (Class *)
                realloc(resolvedFutureClasses, 
                        (resolvedFutureClassCount+1) * sizeof(Class));
            resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
        }
    }
}
  • 自定義一個類HTPerson薪寓,設(shè)置相應(yīng)的斷點
image
  • 執(zhí)行readClass方法后亡资,通過lldb查看cls的值澜共,發(fā)現(xiàn)已經(jīng)跟類名關(guān)聯(lián)上了
image

??我們來探究readClass方法,看看他做了什么操作

readClass方法

這個方法主要是用來更新兩張表:類名稱表gdb_objc_realized_classes沟于, 所有類的表allocatedClasses

image

通過斷點調(diào)試咳胃,我們發(fā)現(xiàn)readClass主要有三步操作:

  • 獲取類名mangledName
  • 將類名和地址關(guān)聯(lián)起來
  • 添加【類和元類】到 所有類的表allocatedClasses表)中,就是runtime_init中開辟的那個表
獲取類名
  • 查看nonlazyMangledName方法旷太,內(nèi)部是通過bites屬性找到ro數(shù)據(jù)展懈,類名存放在class_ro_t結(jié)構(gòu)中
// Get the class's mangled name, or NULL if the class has a lazy
// name that hasn't been created yet.
const char *nonlazyMangledName() const {
    return bits.safe_ro()->getName();
}
  • 查看safe_ro方法,內(nèi)部就是獲取ro結(jié)構(gòu)體數(shù)據(jù)供璧,因為class_ro_t結(jié)構(gòu)體中存儲了類名
const class_ro_t *safe_ro() const {
    class_rw_t *maybe_rw = data();
    if (maybe_rw->flags & RW_REALIZED) {
        // maybe_rw is rw
        // 已實現(xiàn)的類通過 bits -> rw -> ro來獲取
        return maybe_rw->ro();
    } else {
        // maybe_rw is actually ro
        // 未加載過的類bits屬性 存儲的就是 ro結(jié)構(gòu)體數(shù)據(jù)
        return (class_ro_t *)maybe_rw;
    }
}
image
image
addNamedClass將類名和地址關(guān)聯(lián)綁定起來
  • 查看addNamedClass方法存崖,查看類名和地址是如何關(guān)聯(lián)起來
static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
    runtimeLock.assertLocked();
    Class old;
    if ((old = getClassExceptSomeSwift(name))  &&  old != replacing) {
        inform_duplicate(name, old, cls);

        // getMaybeUnrealizedNonMetaClass uses name lookups.
        // Classes not found by name lookup must be in the
        // secondary meta->nonmeta table.
        addNonMetaClass(cls);
    } else {
        // 更新gdb_objc_realized_classes表,將key設(shè)置為 name value 設(shè)置為cls
        NXMapInsert(gdb_objc_realized_classes, name, cls);
    }
    ASSERT(!(cls->data()->flags & RO_META));

    // wrong: constructed classes are already realized when they get here
    // ASSERT(!cls->isRealized());
}
  • 更新gdb_objc_realized_classes哈希表睡毒,keyname来惧,valuecls
添加【類和元類】到 所有類的表中
  • 查看addClassTableEntry方法,源碼如下演顾,這一步就是將類和元類加入到allocatedClasses表中
static void
addClassTableEntry(Class cls, bool addMeta = true)
{
    runtimeLock.assertLocked();

    // This class is allowed to be a known class via the shared cache or via
    // data segments, but it is not allowed to be in the dynamic table already.
    // _objc_init -> runtime_init 中初始化的表:所有類的表
    auto &set = objc::allocatedClasses.get();

    ASSERT(set.find(cls) == set.end());

    if (!isKnownClass(cls))
        set.insert(cls);
    if (addMeta)
        // 將元類插入哈希表中
        addClassTableEntry(cls->ISA(), false);
}

非懶加載類的加載

// +load方法的調(diào)用供搀,是在 load_images方法中
// +load handled by prepare_load_methods()

// 加載非懶加載類
// Realize non-lazy classes (for +load methods and static instances)
for (EACH_HEADER) {
    // 從Mach-O中讀取非懶加載類列表信息,非懶加載類總數(shù)count
    classref_t const *classlist = hi->nlclslist(&count);
    if (hIndex == hCount - 1) {
        _objc_inform("=======加載非懶加載類:HTTest = %zu", count);
    }
    
    for (i = 0; i < count; i++) {
        Class cls = remapClass(classlist[i]);
        if (!cls) continue;
        // 添加【非懶加載類和他的元類】到 所有類的表中
        addClassTableEntry(cls);

        if (cls->isSwiftStable()) {
            if (cls->swiftMetadataInitializer()) {
                _objc_fatal("Swift class %s with a metadata initializer "
                            "is not allowed to be non-lazy",
                            cls->nameForLogging());
            }
            // fixme also disallow relocatable classes
            // We can't disallow all Swift classes because of
            // classes like Swift.__EmptyArrayStorage
        }
        
        realizeClassWithoutSwift(cls, nil);
    }
}
  • 非懶加載類:實現(xiàn)了+ (void)load方法的類钠至,程序啟動時會加載
  • 懶加載類:沒有實現(xiàn)+ (void)load方法的類葛虐,在類首次使用時才會加載
  • 可以查看可執(zhí)行文件Mach-O,在DATA段__objc_nlclslist中存放的是非懶加載類

現(xiàn)在有兩個類:HTPersonHTTeacher棉钧,其中HTPerson類實現(xiàn)了+ (void)load方法屿脐,運行程序,通過MachOView查看可執(zhí)行文件

image

image

  • 設(shè)置相應(yīng)的斷點宪卿,通過lldb打印cls的诵,發(fā)現(xiàn)此時只有一個非懶加載類,即HTPerson類佑钾,地址為0x00000001000082e8西疤,與Mach-O的非懶加載類表相對應(yīng)
image
  • 接下來我們繼續(xù)分析最重要的一個方法,realizeClassWithoutSwift
    image

類的加載

realizeClassWithoutSwift方法分析

realizeClassWithoutSwift對類cls執(zhí)行首次初始化次绘,包括分配其讀寫數(shù)據(jù)(即 rw數(shù)據(jù)瘪阁,用于運行時記錄類的信息),返回類的實際類結(jié)構(gòu)邮偎。

  • 非懶加載類程序啟動時,就會執(zhí)行realizeClassWithoutSwift方法
  • 懶加載類在使用時才會去加載义黎,我們在方法慢速查找時有看到過北秽,執(zhí)行流程:lookUpImpOrForward --> realizeAndInitializeIfNeeded_locked --> realizeClassMaybeSwiftAndLeaveLocked --> realizeClassMaybeSwiftMaybeRelock --> realizeClassWithoutSwift
image
  • realizeClassWithoutSwift方法源碼如下辟拷,代碼比較多,源碼如下
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
    runtimeLock.assertLocked();

    class_rw_t *rw;
    Class supercls;
    Class metacls;

    if (!cls) return nil;
    // 如果類已經(jīng)實現(xiàn)粱快,直接返回
    if (cls->isRealized()) {
        validateAlreadyRealizedClass(cls);
        return cls;
    }
    ASSERT(cls == remapClass(cls));

    // fixme verify class is not in an un-dlopened part of the shared cache?
    // 獲取ro數(shù)據(jù),類未初始化時锅减,類結(jié)構(gòu)objc_class的 bits屬性存儲的其實是 ro數(shù)據(jù)
    auto ro = (const class_ro_t *)cls->data();
    auto isMeta = ro->flags & RO_META;
    if (ro->flags & RO_FUTURE) {
        // This was a future class. rw data is already allocated.
        rw = cls->data();
        ro = cls->data()->ro();
        ASSERT(!isMeta);
        cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    } else {
        // Normal class. Allocate writeable class data.
        // 開辟可讀寫數(shù)據(jù),即rw,bits屬性此時存儲的是 rw數(shù)據(jù)
        rw = objc::zalloc<class_rw_t>();
        rw->set_ro(ro);
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        cls->setData(rw);
    }

    cls->cache.initializeToEmptyOrPreoptimizedInDisguise();

#if FAST_CACHE_META
    if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif

    // Choose an index for this class.
    // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
    cls->chooseClassArrayIndex();

    if (PrintConnecting) {
        _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
                     cls->nameForLogging(), isMeta ? " (meta)" : "", 
                     (void*)cls, ro, cls->classArrayIndex(),
                     cls->isSwiftStable() ? "(swift)" : "",
                     cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
    }

    // Realize superclass and metaclass, if they aren't already.
    // This needs to be done after RW_REALIZED is set above, for root classes.
    // This needs to be done after class index is chosen, for root metaclasses.
    // This assumes that none of those classes have Swift contents,
    //   or that Swift's initializers have already been called.
    //   fixme that assumption will be wrong if we add support
    //   for ObjC subclasses of Swift classes.
    // 遞歸實現(xiàn) 父類和元類
    supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);

#if SUPPORT_NONPOINTER_ISA
    if (isMeta) {
        // Metaclasses do not need any features from non pointer ISA
        // This allows for a faspath for classes in objc_retain/objc_release.
        cls->setInstancesRequireRawIsa();
    } else {
        // Disable non-pointer isa for some classes and/or platforms.
        // Set instancesRequireRawIsa.
        bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
        bool rawIsaIsInherited = false;
        static bool hackedDispatch = false;

        if (DisableNonpointerIsa) {
            // Non-pointer isa disabled by environment or app SDK version
            instancesRequireRawIsa = true;
        }
        else if (!hackedDispatch  &&  0 == strcmp(ro->getName(), "OS_object"))
        {
            // hack for libdispatch et al - isa also acts as vtable pointer
            hackedDispatch = true;
            instancesRequireRawIsa = true;
        }
        else if (supercls  &&  supercls->getSuperclass()  &&
                 supercls->instancesRequireRawIsa())
        {
            // This is also propagated by addSubclass()
            // but nonpointer isa setup needs it earlier.
            // Special case: instancesRequireRawIsa does not propagate
            // from root class to root metaclass
            instancesRequireRawIsa = true;
            rawIsaIsInherited = true;
        }

        if (instancesRequireRawIsa) {
            cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
        }
    }
// SUPPORT_NONPOINTER_ISA
#endif

    // Update superclass and metaclass in case of remapping
    // 設(shè)置superclass 和 isa
    cls->setSuperclass(supercls);
    cls->initClassIsa(metacls);

    // Reconcile instance variable offsets / layout.
    // This may reallocate class_ro_t, updating our ro variable.
    if (supercls  &&  !isMeta) reconcileInstanceVariables(cls, supercls, ro);

    // Set fastInstanceSize if it wasn't set already.
    // 設(shè)置fastInstanceSize卸夕,編譯器快速計算對象內(nèi)存大小
    cls->setInstanceSize(ro->instanceSize);

    // Copy some flags from ro to rw
    if (ro->flags & RO_HAS_CXX_STRUCTORS) {
        cls->setHasCxxDtor();
        if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
            cls->setHasCxxCtor();
        }
    }
    
    // Propagate the associated objects forbidden flag from ro or from
    // the superclass.
    if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
        (supercls && supercls->forbidsAssociatedObjects()))
    {
        rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
    }

    // Connect this class to its superclass's subclass lists
    // 建立類 子類的雙向鏈表關(guān)系
    if (supercls) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }

    // Attach categories
    // 附加類別 - 方法化當前的類,方法排序婆瓜,對類進行擴展
    methodizeClass(cls, previously);

    return cls;
}

realizeClassWithoutSwift主要進行了下列幾步操作:
1快集、rw初始化,這里涉及到干凈內(nèi)存clean memory臟內(nèi)存dirty memory的概念廉白。

  • ro屬于clean memory个初,在編譯時即確定的內(nèi)存空間,只讀猴蹂,加載后不會發(fā)生改變的內(nèi)存空間院溺,包括類名稱、方法磅轻、協(xié)議和實例變量的信息珍逸;
  • rw的數(shù)據(jù)空間屬于dirty memoryrw是運行時的結(jié)構(gòu)聋溜,可讀可寫谆膳,由于其動態(tài)性,可以往類中添加屬性勤婚、方法摹量、協(xié)議。在運行時會發(fā)生變更的內(nèi)存馒胆。
  • rwe類的額外信息缨称。在WWDC2020中也提到,只有不到10%的類真正的更改了他們的方法祝迂,并不是每一個類都需要插入數(shù)據(jù)睦尽,進行修改的類很少,避免資源的消耗型雳,所以就有了rwe当凡。
image
image

2、遞歸處理纠俭,進行父類和元類的實現(xiàn)沿量。


image

3、isa處理冤荆,在前面學(xué)習(xí)isa的時候朴则,對于NONPOINTER_ISA進行了位域處理,指針優(yōu)化钓简,isa的末尾位是1乌妒,isa不單單代表一個指針汹想。而對于元類以及特殊情況下的場景的一些類,無需開啟指針優(yōu)化的類撤蚊,使用Raw Isa古掏,isa的末尾位是0。

image

4侦啸、設(shè)置superclassisa屬性槽唾,用來獲取父類和元類

image

5、設(shè)置fastInstanceSize匹中,編譯器快速計算對象內(nèi)存大小

image

6夏漱、c++析構(gòu)函數(shù)的相關(guān)設(shè)置,以及關(guān)聯(lián)對象的相關(guān)設(shè)置顶捷。

image

7挂绰、建立子類與父類的雙定鏈表關(guān)系,保證子類能找到父類服赎,父類也可以找到子類葵蒂。

image

8、方法化當前的類重虑,向類中添加方法践付,協(xié)議、屬性缺厉,同時對方法列表進行排序等操作永高。


image

methodizeClass方法分析

methodizeClass方法源碼如下:

static void methodizeClass(Class cls, Class previously)
{
    runtimeLock.assertLocked();

    bool isMeta = cls->isMetaClass();
    auto rw = cls->data();
    auto ro = rw->ro();
    auto rwe = rw->ext();

    // Methodizing for the first time
    if (PrintConnecting) {
        _objc_inform("CLASS: methodizing class '%s' %s", 
                     cls->nameForLogging(), isMeta ? "(meta)" : "");
    }

    // Install methods and properties that the class implements itself.
    method_list_t *list = ro->baseMethods();
    if (list) {
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
        if (rwe) rwe->methods.attachLists(&list, 1);
    }

    property_list_t *proplist = ro->baseProperties;
    if (rwe && proplist) {
        rwe->properties.attachLists(&proplist, 1);
    }

    protocol_list_t *protolist = ro->baseProtocols;
    if (rwe && protolist) {
        rwe->protocols.attachLists(&protolist, 1);
    }

    // Root classes get bonus method implementations if they don't have 
    // them already. These apply before category replacements.
    if (cls->isRootMetaclass()) {
        // root metaclass
        addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
    }

    // Attach categories.
    if (previously) {
        if (isMeta) {
            objc::unattachedCategories.attachToClass(cls, previously,
                                                     ATTACH_METACLASS);
        } else {
            // When a class relocates, categories with class methods
            // may be registered on the class itself rather than on
            // the metaclass. Tell attachToClass to look for those.
            objc::unattachedCategories.attachToClass(cls, previously,
                                                     ATTACH_CLASS_AND_METACLASS);
        }
    }
    objc::unattachedCategories.attachToClass(cls, cls,
                                             isMeta ? ATTACH_METACLASS : ATTACH_CLASS);

#if DEBUG
    // Debug: sanity-check all SELs; log method list contents
    for (const auto& meth : rw->methods()) {
        if (PrintConnecting) {
            _objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(meth.name()));
        }
        ASSERT(sel_registerName(sel_getName(meth.name())) == meth.name());
    }
#endif
}
  • 1、對方法提针、屬性命爬、協(xié)議進行處理。見下圖:

    image

    從上圖可以發(fā)現(xiàn):rwe為空辐脖,很顯然饲宛,此時還沒有對類進行相關(guān)的擴展操作,所以rwe還沒有被創(chuàng)建初始化嗜价。此時針對方法艇抠、屬性、協(xié)議的添加操作時無效的久锥!

  • 2家淤、方法列表的處理中有些不同,調(diào)用了prepareMethodLists方法瑟由。那么該方法做了哪些操作呢媒鼓?見下圖:

image

核心流程,fixupMethodList错妖,根據(jù)注釋:根據(jù)需要對selector進行修復(fù)绿鸣。進入fixupMethodList方法,查看實現(xiàn)流程暂氯。見下圖:

image

  • 繼續(xù)methodizeClass源碼的解讀潮模。找到了類初始化過程中非常關(guān)鍵的步驟,向類中添加分類方法痴施、協(xié)議等擎厢,rwe的初始化也在其中。
image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末辣吃,一起剝皮案震驚了整個濱河市动遭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌神得,老刑警劉巖厘惦,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異哩簿,居然都是意外死亡宵蕉,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門节榜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來羡玛,“玉大人,你說我怎么就攤上這事宗苍〖诟澹” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵讳窟,是天一觀的道長让歼。 經(jīng)常有香客問我,道長挪钓,這世上最難降的妖魔是什么是越? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮碌上,結(jié)果婚禮上倚评,老公的妹妹穿的比我還像新娘。我一直安慰自己馏予,他們只是感情好天梧,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著霞丧,像睡著了一般呢岗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天后豫,我揣著相機與錄音悉尾,去河邊找鬼。 笑死挫酿,一個胖子當著我的面吹牛构眯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播早龟,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼惫霸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了葱弟?” 一聲冷哼從身側(cè)響起壹店,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎芝加,沒想到半個月后硅卢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡妖混,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年老赤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片制市。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡抬旺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出祥楣,到底是詐尸還是另有隱情开财,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布误褪,位于F島的核電站责鳍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏兽间。R本人自食惡果不足惜历葛,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嘀略。 院中可真熱鬧恤溶,春花似錦、人聲如沸帜羊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽讼育。三九已至帐姻,卻和暖如春稠集,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背饥瓷。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工剥纷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人扛伍。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓筷畦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親刺洒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

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