iOS底層探索之map_images

在上一篇文章 objc_init 分析 中,最后有三個(gè)函數(shù) map_images莺奔、load_imagesunmap_image愚隧。

一钥组、map_images

/***********************************************************************
* 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);
}
1.1 map_images_nolock
void 
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                  const struct mach_header * const mhdrs[])
{
    // 省略準(zhǔn)備邏輯...

    if (hCount > 0) {
        _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
    }

    firstTime = NO;
    
    // Call image load funcs after everything is set up.
    for (auto func : loadImageFuncs) {
        for (uint32_t i = 0; i < mhCount; i++) {
            func(mhdrs[i]);
        }
    }
}

因?yàn)榇a比較多信夫,省略了很多代碼,瀏覽代碼后铣减,會(huì)發(fā)現(xiàn)重要的是 _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses); 這一行代碼她君。

二、_read_images

因?yàn)樵创a比較長(zhǎng)葫哗,這里就不貼了缔刹。

void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
    // 省略代碼...

    // Discover classes. Fix up unresolved future classes. Mark bundle classes.
    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;
        }

        classref_t const *classlist = _getObjc2ClassList(hi, &count);

        bool headerIsBundle = hi->isBundle();
        bool headerIsPreoptimized = hi->hasPreoptimizedClasses();

        for (i = 0; i < count; i++) {
            // 斷點(diǎn)1
            Class cls = (Class)classlist[i];
            Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);  // 讀取類
        }
    }
    // 省略 protocol 處理...

    // Realize non-lazy classes (for +load methods and static instances) 
    // 實(shí)現(xiàn)非惰性類(用于+ load方法和靜態(tài)實(shí)例)
    for (EACH_HEADER) {
        classref_t const *classlist = 
            _getObjc2NonlazyClassList(hi, &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);
        }
    }
}

主要流程如下:

  1. 條件控制進(jìn)行一次的加載
  2. 修復(fù)預(yù)編譯階段的 @selector 的混亂問(wèn)題
  3. 錯(cuò)誤混亂的類處理
  4. 修復(fù)重映射?些沒(méi)有被鏡像?件加載進(jìn)來(lái)的類
  5. 修復(fù)?些消息
  6. 當(dāng)我們類??有協(xié)議的時(shí)候 : readProtocol
  7. 修復(fù)沒(méi)有被加載的協(xié)議
  8. 分類處理
  9. 類的加載處理
  10. 沒(méi)有被處理的類 優(yōu)化那些被侵犯的類

在上面代碼中 斷點(diǎn)1 打個(gè)斷點(diǎn),運(yùn)行

(lldb) po cls
0x000000010048dfe8
--- 走過(guò)斷點(diǎn)1和readClass之后再打印
(lldb) po newCls
OS_dispatch_io

在沒(méi)有 readClass 之前劣针,classlist 里面存的還是內(nèi)存地址校镐,readClass 之后,就有名字了捺典。下面看下 readClass 具體做了什么?

2.1 readClass

讀取編譯器編寫的類和元類鸟廓。返回新的類指針。

Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
    const char *mangledName = cls->mangledName();
    
    if (missingWeakSuperclass(cls)) {
        // 省略...
    }
    
    cls->fixupBackwardDeployingStableSwift();

    Class replacing = nil;
    if (Class newCls = popFutureNamedClass(mangledName)) {
        // 省略...
    }
    
    if (headerIsPreoptimized  &&  !replacing) {
        // class list built in shared cache
        // fixme strict assert doesn't work because of duplicates
        // ASSERT(cls == getClass(name));
        ASSERT(getClassExceptSomeSwift(mangledName));
    } else {
        // 重點(diǎn)
        addNamedClass(cls, mangledName, replacing);
        addClassTableEntry(cls);
    }

    // for future reference: shared cache never contains MH_BUNDLEs
    if (headerIsBundle) {
        cls->data()->flags |= RO_FROM_BUNDLE;
        cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
    }
    
    return cls;
}

mangleName 打個(gè)斷點(diǎn)

mangledName斷點(diǎn).png

發(fā)現(xiàn)是系統(tǒng)的類。我們還是研究自定義類 GLPerson引谜,在mangleName 下面添加如下代碼:

    // godlong test begin
    const char *GLPersonName = "GLPerson";
    if (strcmp(mangledName, GLPersonName) == 0) {
        printf("%s 來(lái)到了自定義類 %s", __func__, mangledName);
    }
    // godlong test end

斷點(diǎn)到 if 條件里面牍陌,運(yùn)行工程,成功進(jìn)入斷點(diǎn)

進(jìn)入自定義類.png

2.2 addNamedClass

走到 addNamedClass 斷點(diǎn)查看

addNamedClass.png

可以發(fā)現(xiàn)這時(shí) cls 還是地址员咽,而 mangledName 就是我們的類名毒涧。

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 {
        NXMapInsert(gdb_objc_realized_classes, name, cls);
    }
    ASSERT(!(cls->data()->flags & RO_META));
}

解釋將類的名稱添加到非元類的類映射表中。警告有關(guān)重復(fù)的類名贝室,并保留舊的映射契讲。

2.3 addClassTableEntry
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.
    auto &set = objc::allocatedClasses.get();

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

    if (!isKnownClass(cls))
        set.insert(cls);
    if (addMeta)
        addClassTableEntry(cls->ISA(), false);
}

解釋 : 將一個(gè)類添加到所有類的表中。如果 addMetatrue滑频,也自動(dòng)添加該類的元類捡偏。

三、realizeClassWithoutSwift

先重點(diǎn)關(guān)注的加載峡迷,在 read_images 的源碼中银伟,往下看的時(shí)候,發(fā)現(xiàn)注釋

Realize non-lazy classes (for +load methods and static instances)
【譯】實(shí)現(xiàn)非懶加載類(用于+ load方法和靜態(tài)實(shí)例)

那什么是 non-lazy classes 呢凉当?

3.1 non-lazy classes 非懶加載類

如果一個(gè)類實(shí)現(xiàn)了 + load 方法枣申,那這個(gè)類就是 non-lazy class(非懶加載類)。

反之看杭,沒(méi)實(shí)現(xiàn) + load 方法忠藤,就是懶加載類。

3.2 進(jìn)入自定義類的 realizeClassWithoutSwift

GLPerson 類添加 + load 方法楼雹,然后把 for(EACH_HEADER) 循環(huán)里面的代碼改成如下:

    // Realize non-lazy classes (for +load methods and static instances)
    for (EACH_HEADER) {
        classref_t const *classlist = 
            _getObjc2NonlazyClassList(hi, &count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            if (!cls) continue;
            
            // loong test begin
            const char *mangledName = cls->mangledName();
            const char *GLPersonName = "GLPerson";
            if (strcmp(mangledName, GLPersonName) == 0) {
                printf("%s 來(lái)到了自定義類 %s", __func__, mangledName); // 斷點(diǎn)2
            }
            // loong test end

            addClassTableEntry(cls);
            // class is a Swift class from the stable Swift ABI 所以不會(huì)走這里
            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);
        }
    }

斷點(diǎn)到上面代碼的 斷點(diǎn)2模孩,確認(rèn)來(lái)到了自定義類 GLPerson

斷點(diǎn)2.png
3.3【重點(diǎn)】realizeClassWithoutSwift 源碼

realizeClassWithoutSwift 雖然很長(zhǎng)贮缅,但是都比較重要榨咐,所以也沒(méi)有省略,全貼出來(lái)了谴供。

方法注釋:

  • Performs first-time initialization on class cls,
  • including allocating its read-write data.
  • Does not perform any Swift-side initialization.
  • Returns the real class structure for the class.
    【譯】* 對(duì)類 cls 進(jìn)行首次初始化块茁,包括分配其讀寫數(shù)據(jù)。不執(zhí)行任何 Swift 端初始化桂肌。返回該類的真實(shí)類結(jié)構(gòu)数焊。
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
    runtimeLock.assertLocked();

    class_rw_t *rw; // 讀寫數(shù)據(jù)
    Class supercls; // 父類
    Class metacls;  // 元類

    if (!cls) return nil; // 如果為空,返回nil
    if (cls->isRealized()) return cls;  // 如果已經(jīng)實(shí)現(xiàn)崎场,直接返回
    ASSERT(cls == remapClass(cls));

    // fixme verify class is not in an un-dlopened part of the shared cache?

    auto ro = (const class_ro_t *)cls->data(); // 讀取類的數(shù)據(jù)
    auto isMeta = ro->flags & RO_META; // 是否是元類
    if (ro->flags & RO_FUTURE) { // rw已經(jīng)有值的話走這里
        // 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.
        rw = objc::zalloc<class_rw_t>(); // 開(kāi)辟rw
        rw->set_ro(ro); // 把cls的數(shù)據(jù)ro賦值給rw
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta; // 更新flags
        cls->setData(rw); // 再把rw設(shè)置為cls的data數(shù)據(jù)
    }

#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.
    //實(shí)現(xiàn)超類和元類(如果尚未實(shí)現(xiàn))佩耳。
    // This needs to be done after RW_REALIZED is set above, for root classes.
    //對(duì)于根類,需要在上面設(shè)置了RW_REALIZED之后執(zhí)行此操作谭跨。
    // This needs to be done after class index is chosen, for root metaclasses.
    //對(duì)于根元類干厚,需要在選擇類索引之后執(zhí)行此操作李滴。
    // 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.
    // 遞歸調(diào)用 realizeClassWithoutSwift ,實(shí)現(xiàn)父類和元類
    supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);

#if SUPPORT_NONPOINTER_ISA
    if (isMeta) { // 如果是元類蛮瞄,對(duì)isa處理
        // 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 { // 不是元類所坯,也是對(duì)isa處理
        // 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->name, "OS_object"))
        {
            // hack for libdispatch et al - isa also acts as vtable pointer
            hackedDispatch = true;
            instancesRequireRawIsa = true;
        }
        else if (supercls  &&  supercls->superclass  &&
                 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
    // 確定繼承鏈,賦值父類和元類
    cls->superclass = supercls;
    cls->initClassIsa(metacls);

    // Reconcile instance variable offsets / layout.
     // 協(xié)調(diào)實(shí)例變量的偏移量/布局裕坊。
    // 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.
    // 經(jīng)過(guò)上一步包竹,再次協(xié)調(diào)屬性對(duì)齊后,設(shè)置實(shí)例大小
    cls->setInstanceSize(ro->instanceSize);

    // Copy some flags from ro to rw
    // 賦值一些 ro 中的 flags標(biāo)識(shí)位 到 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.
    // 從ro或父類傳播關(guān)聯(lián)的對(duì)象禁止標(biāo)志籍凝。
    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
    // 添加當(dāng)前類到父類的子類列表中,如果沒(méi)有父類苗缩,設(shè)置自己就是根類
    if (supercls) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }

    // Attach categories
    // 附加分類
    methodizeClass(cls, previously);

    return cls;
}

確認(rèn)是 GLPerson 的時(shí)候進(jìn)入 realizeClassWithoutSwift
饵蒂,在 auto ro = (const class_ro_t *)cls->data(); 這行代碼打一個(gè)端點(diǎn),這一步是 cls 讀取 data 數(shù)據(jù)賦值給 ro酱讶。

然后往下走一步退盯,看看讀取到了什么數(shù)據(jù)?

類的ro數(shù)據(jù)查看.png
3.4 遞歸調(diào)用 realizeClassWithoutSwift
    supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);

    // Update superclass and metaclass in case of remapping
    cls->superclass = supercls;
    cls->initClassIsa(metacls);

realizeClassWithoutSwift 的源碼中泻肯,遞歸調(diào)用了 realizeClassWithoutSwift渊迁,分別傳入的是父類和元類(ISA())。

這是為了確定繼承鏈的關(guān)系灶挟。

3.5 懶加載類實(shí)現(xiàn)時(shí)機(jī)

知道了非懶加載類在 map_images 時(shí)琉朽,調(diào)用 realizeClassWithoutSwift 實(shí)現(xiàn)的,那懶加載類是什么時(shí)候?qū)崿F(xiàn)的呢稚铣?

還是上面的代碼箱叁,把 GLPerson 中的 + load 方法去掉,然后再realizeClassWithoutSwift 方法里面添加如下代碼并斷點(diǎn)在 斷點(diǎn)3

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

    class_rw_t *rw;
    Class supercls;
    Class metacls;
    
    // loong test begin
    if (cls) {
        const char *mangledName = cls->mangledName();
        const char *GLPersonName = "GLPerson";
        if (strcmp(mangledName, GLPersonName) == 0) {
            printf("%s 來(lái)到了自定義類 %s", __func__, mangledName); // 斷點(diǎn)3
        }
    }
    // loong test end
    // 省略...
}

main.m 中添加惕医,并添加斷點(diǎn)

        GLPerson *p = [GLPerson alloc];

運(yùn)行工程耕漱,首先斷點(diǎn)在 GLPerson *p = [GLPerson alloc];

繼續(xù)運(yùn)行,發(fā)現(xiàn)走到了 斷點(diǎn)3抬伺。查看堆棧

懶加載類加載堆棧.png

可知:懶加載類 是在第一次發(fā)送消息的時(shí)候螟够,調(diào)用 realizeClassWithoutSwift 實(shí)現(xiàn)類的。

3.6 懶加載類的優(yōu)點(diǎn)

因?yàn)檎5墓こ讨邢康觯瑢?shí)現(xiàn) + load 方法的類很少妓笙,大部分類都是懶加載類。

如果所有的類都在啟動(dòng)的時(shí)候?qū)崿F(xiàn)完成椒楣,就會(huì)非常慢给郊,懶加載類等到調(diào)用的時(shí)候再去實(shí)現(xiàn),這樣會(huì)加快啟動(dòng)速度捧灰。

再就是每個(gè)類都有很多的代碼淆九,包括變量统锤,方法等,會(huì)占用很多內(nèi)存炭庙,而如果你這個(gè)類在工程中就沒(méi)調(diào)用饲窿,或者在很深的頁(yè)面才會(huì)調(diào)用,正常情況下很少人會(huì)使用到焕蹄,如果在啟動(dòng)加載了逾雄,就會(huì)造成內(nèi)存浪費(fèi)。

懶加載類優(yōu)點(diǎn):

  • 加快啟動(dòng)速度
  • 節(jié)省應(yīng)用初始內(nèi)存

四腻脏、methodizeClass 附加分類

作用:修復(fù) cls 的方法列表鸦泳,協(xié)議列表和屬性列表。附加任何未解決的分類永品。

源碼:

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

    bool isMeta = cls->isMetaClass();
    auto rw = cls->data();
    auto ro = rw->ro(); // 讀取ro數(shù)據(jù)
    auto rwe = rw->ext(); // 讀取ext做鹰,賦值給rwe

    // 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(); // 獲取ro中的方法列表
    if (list) {
        // 對(duì)方法列表list重新排序
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
        if (rwe) rwe->methods.attachLists(&list, 1); // 如果有rwe,添加方法列表list到rwe的methodsList
    }

    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 根元類添加initialize方法
        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
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鼎姐,一起剝皮案震驚了整個(gè)濱河市钾麸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌炕桨,老刑警劉巖饭尝,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異献宫,居然都是意外死亡钥平,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門遵蚜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)帖池,“玉大人,你說(shuō)我怎么就攤上這事吭净∷冢” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵寂殉,是天一觀的道長(zhǎng)囚巴。 經(jīng)常有香客問(wèn)我,道長(zhǎng)友扰,這世上最難降的妖魔是什么彤叉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮村怪,結(jié)果婚禮上秽浇,老公的妹妹穿的比我還像新娘。我一直安慰自己甚负,他們只是感情好柬焕,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布审残。 她就那樣靜靜地躺著,像睡著了一般斑举。 火紅的嫁衣襯著肌膚如雪搅轿。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,792評(píng)論 1 290
  • 那天富玷,我揣著相機(jī)與錄音璧坟,去河邊找鬼。 笑死赎懦,一個(gè)胖子當(dāng)著我的面吹牛雀鹃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播铲敛,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼褐澎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了伐蒋?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤迁酸,失蹤者是張志新(化名)和其女友劉穎先鱼,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體奸鬓,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡焙畔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了串远。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宏多。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖澡罚,靈堂內(nèi)的尸體忽然破棺而出伸但,到底是詐尸還是另有隱情,我是刑警寧澤留搔,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布更胖,位于F島的核電站,受9級(jí)特大地震影響隔显,放射性物質(zhì)發(fā)生泄漏却妨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一括眠、第九天 我趴在偏房一處隱蔽的房頂上張望彪标。 院中可真熱鬧,春花似錦掷豺、人聲如沸捞烟。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)坷襟。三九已至奸柬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間婴程,已是汗流浹背廓奕。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留档叔,地道東北人桌粉。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像衙四,于是被迫代替她去往敵國(guó)和親铃肯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

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