Class 內(nèi)部結(jié)構(gòu)

isa, cache, bits

通過前面一篇從 MachO 加載到對(duì)象創(chuàng)建! 可以了解到:
  • 在 alloc 的時(shí)候, 系統(tǒng)會(huì)開辟一片內(nèi)存空間, 最終以指針的形式返回(對(duì)象).
    而后便會(huì)初始化(對(duì)象的) isa ==> initIsa(cls), 具體可以根據(jù)源碼編譯跟進(jìn)調(diào)試, 作為前面文章一個(gè)補(bǔ)充:

如果只看 Class 可跳過這一步.

編譯 objc-750

首先從官方資源網(wǎng)下載 objc 的源碼, 打開 objc.xcodeproj 文件然后進(jìn)行編譯 :

報(bào)錯(cuò)1:

The i386 architecture is deprecated. 
You should update your ARCHS build setting to \
remove the i386 architecture. 
(in target 'objc')

'i8386' 架構(gòu)被廢棄, 你應(yīng)該更新你的 ARCHS 編譯設(shè)置項(xiàng), 刪除 'i8386' 架構(gòu).
來到 target objcbuild setting, 搜索 ARCHS, 會(huì)發(fā)現(xiàn)報(bào)錯(cuò)提示的 'i386' 以及 'x86_64', 刪除 'i386' 再編譯 :

報(bào)錯(cuò)2:

The i386 architecture is deprecated. 
You should update your ARCHS build setting to \
remove the i386 architecture. 
(in target 'objc-trampolines')

同報(bào)錯(cuò)1, 將 target objc-trampolinesbuild setting下 'i386' 刪除再編譯 (注: 這里可以將 objc-trampolinesobjc-simulator 兩個(gè) target 刪除, 因?yàn)橐话阌貌坏? :

報(bào)錯(cuò)3

'sys/reason.h' file not found

這里需要將頭文件 (注:需要下載多個(gè)依賴庫, 其中包含想要的頭文件, 比如 reason.h./xnu-4903.221.2/bsd/sys/reason.h 中, 所以需要下載該庫以獲取頭文件) 包含到任一新建文件夾下, 并將系統(tǒng)頭文件路徑 system header search paths 設(shè)置為該文件夾目錄, 具體可參照:
最新Runtime源碼objc4-750編譯

注: 此處并不需要依次建立文件夾, 直接放到工程目錄下, 把包含的頭文件路徑換成頭文件(即: <sys/reason.h> 修改為 <reason.h>) 也可以, 只要 system header search paths + import 頭文件的絕對(duì)路徑能找到就可以.

報(bào)錯(cuò)4

Use of undeclared identifier 'CRGetCrashLogMessage'

查看依賴 CrashReporterClient.h 頭文件, 發(fā)現(xiàn)此處用了宏定義:

#ifdef LIBC_NO_LIBCRASHREPORTERCLIENT

/* Fake the CrashReporterClient API */
#define CRGetCrashLogMessage() 0
#define CRSetCrashLogMessage(x) /* nothing */

#else /* !LIBC_NO_LIBCRASHREPORTERCLIENT */

所以需要在 Build SettingsPreprocessor Macros(預(yù)處理宏)類目中加入 LIBC_NO_LIBCRASHREPORTERCLIENT 環(huán)境變量來使方法生效, 繼續(xù)編譯:

報(bào)錯(cuò)5

clang:-1: linker command failed with exit code 1 

這個(gè)報(bào)錯(cuò)很多人摸不著頭腦, 因?yàn)闆]有報(bào)錯(cuò)信息, 只知道是 link 時(shí)報(bào)錯(cuò), 這里有個(gè)技巧:
就是查看編譯日志 (快捷鍵 command 9), 這里記錄詳細(xì)的報(bào)錯(cuò)信息:

ld: can't open order file: 
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform
/Developer/SDKs/MacOSX10.14.sdk/AppleInternal/OrderFiles/libobjc.order
clang: error: linker command failed with exit code 1

打不開 order file 文件 (因?yàn)檎也坏?, 別擔(dān)心, 只需要換個(gè)路徑就可以了, 在工程目錄的 other 文件夾下同樣有一份這個(gè)文件, 同樣修改 Build Settings 設(shè)置, 搜索 order file, 即可看到原來是查找 AppleInternal目錄下的文件, 替換為工程目錄下的文件路徑繼續(xù)編譯:

報(bào)錯(cuò)6

clang:-1: linker command failed with exit code 1

同樣是 clang 錯(cuò)誤, 這下知道怎么看詳細(xì)的報(bào)錯(cuò)信息了, 那么:

ld: library not found for -lCrashReporterClient
clang: error: linker command failed with exit code 1

報(bào)錯(cuò)找不到該庫文件, 由于我們前面設(shè)置過 LIBC_NO_LIBCRASHREPORTERCLIENT 環(huán)境變量, 所以這個(gè) -lCrashReporterClient 是不需要的, 由于報(bào)錯(cuò)是 linker command, 所以要到 Build Settings 里面搜索 linking, 可以看見 Other link flags 里面有設(shè)置這樣的 flag (注: 也可以直接搜索 lCrashReporterClient 關(guān)鍵字, 搜索結(jié)果會(huì)把包含所有 lCrashReporterClient flag 的類目找出來) , 刪除該 flag 即可.
還是繼續(xù)編譯:

報(bào)錯(cuò)7

/xcodebuild:-1: SDK "macosx.internal" cannot be located.
/xcrun:-1: unable to find utility "clang++", 
not a developer tool or in PATH

看著 xcodebuildxcrun 報(bào)錯(cuò), 又是一臉懵, 其實(shí)很簡單, 還是查看編譯日志:

+ /usr/bin/xcrun -sdk macosx.internal clang++ \
-Wall -mmacosx-version-min=10.12 
-arch x86_64 -std=c++11 
PATH
xcodebuild: error: SDK "macosx.internal" cannot be located.
xcrun: error: unable to find utility "clang++", \
not a developer tool or in PATH

xcodebuild: error: "macosx.internal" SDK 找不到, 找不到 "clang++" 命令, 明顯的還有執(zhí)行的命令(如下圖):

image.png

原來是在執(zhí)行 script 時(shí)報(bào)錯(cuò), 那么就來到 build phases 中查看執(zhí)行的 script 信息:

set -x
/usr/bin/xcrun -sdk macosx.internal clang++ -Wall \
-mmacosx-version-min=10.12 
-arch x86_64 -std=c++11 "${SRCROOT}/markgc.cpp" -o 
"${BUILT_PRODUCTS_DIR}/markgc"
"${BUILT_PRODUCTS_DIR}/markgc" "${BUILT_PRODUCTS_DIR}/libobjc.A.dylib"

腳本旨在調(diào)用 clang++ 命令, 所以把 macosx.internal 改為 macosx, 使用系統(tǒng)自帶的 clang 命令進(jìn)行編譯,
再編譯:

報(bào)錯(cuò)8

no such public header file: '/tmp/objc.dst/usr/include/objc/ObjectiveC\
.apinotes'

同樣, 查看編譯日志:


image.png

由于是在 InstallAPI 時(shí)報(bào)錯(cuò), 所以可以在 build setting 中查詢關(guān)鍵字 InstallAPI, 直接將 Supports Text-Based InstallAPI 設(shè)置為 NO (注: 這里也可以將 InstallAPI flags 中對(duì)應(yīng)的 flag 刪除以消除編譯錯(cuò)誤, 需要相繼刪除幾個(gè)).

編譯成功
到這里, 編譯已經(jīng)成功了, 那么接下來可以新建測試項(xiàng)目了:
此時(shí)應(yīng)注意, 編譯環(huán)境只是在 mac 下, 所以新建 Target 時(shí)只能選擇 macOS 下的Application 相關(guān)項(xiàng) (Cocoa App, Game, Command Line Tool 等), 然后添加對(duì)剛剛配置好的 objc 庫依賴 (如果需要的話).

Class

有源碼文件可見:
Class 定義為 typedef struct objc_class *Class;, 是一個(gè)指向 objc_class 的結(jié)構(gòu)體指針;

objc_class

包含類的 isa, 父類, 緩存等信息:

struct objc_class : objc_object {
    // Class ISA; 繼承自 objc_object
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
}

依次:

isa

在類實(shí)現(xiàn)方法中 static Class realizeClass(Class cls), 在設(shè)置好 cls->superclass 后便會(huì)進(jìn)行 isa 初始化 cls->initClassIsa(metacls)(文章開頭已給出鏈接, 不作贅述):
最終調(diào)用:

inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) {

    if (!nonpointer) {
        isa.cls = cls;
    } else {
        isa_t newisa(0);
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3;
        isa = newisa;
    }
}

對(duì) isa 簡單初始化并賦值 cls 的信息, 那么 isa 到底是什么?

解析 isa (附 superClass):
調(diào)用如下函數(shù)(方法):

// isa 流程圖驗(yàn)證
int isaTest() {
    WXPerson *person = [[WXPerson alloc] init];
    Class cls = person.class; // 類對(duì)象
    Class cls4 = object_getClass(cls); // 元類
    Class cls5 = object_getClass(cls4); // 根元類
    NSLog(@"%@ -- %p 對(duì)象", person, person);
    NSLog(@"%@ -- %p 類對(duì)象", cls,cls);
    NSLog(@"%@ -- %p 元類", cls4, cls4);
    NSLog(@"%@ -- %p 根元類", cls5, cls5); // 根元類
    NSLog(@"%@ -- %p 對(duì)象父類", person.superclass, person.superclass);
    NSLog(@"%@ -- %p 類對(duì)象父類",[cls superclass] , [cls superclass]);
    NSLog(@"%@ -- %p 元類父類", [cls4 superclass], [cls4 superclass]);
    NSLog(@"%@ -- %p 根元類父類", [cls5 superclass], [cls5 superclass]);
    NSLog(@"%@ -- %p 根元類的isa", object_getClass(cls5), 
                                  object_getClass(cls5));
    
    NSLog(@"%@ -- %p NSObject 父類", [[[NSObject alloc] init] superclass],
                                    [[[NSObject alloc] init] superclass]);
                                    
    NSLog(@"%@ -- %p NSObject isa", object_getClass([NSObject class]),
                                    object_getClass([NSObject class]));
    
    return 0;
}

這里直接貼下 log :

2019-04-03 11:36 [51997:6246754] <WXPerson: 0x100f4bd20> --0x100f4bd20 對(duì)象
2019-04-03 11:36 [51997:6246754] WXPerson -- 0x1000026d8 類對(duì)象        
2019-04-03 11:36 [51997:6246754] WXPerson -- 0x1000026b0 元類          
2019-04-03 11:36 [51997:6246754] NSObject -- 0x100b170f0 根元類        
2019-04-03 11:36 [51997:6246754] WXHuman  -- 0x100002688 對(duì)象父類       
2019-04-03 11:36 [51997:6246754] WXHuman  -- 0x100002688 類對(duì)象父類     
2019-04-03 11:36 [51997:6246754] WXHuman  -- 0x100002660 元類父類       
2019-04-03 11:36 [51997:6246754] NSObject -- 0x100b17140 根元類父類     
2019-04-03 11:36 [51997:6246754] NSObject -- 0x100b170f0 根元類的isa    
2019-04-03 11:36 [51997:6246754] (null)   -- 0x0         NSObject 父類  
2019-04-03 11:36 [51997:6246754] NSObject -- 0x100b170f0 NSObject isa

這里在貼一張根據(jù)打印地址畫的一張 isa 走向圖 (地址對(duì)號(hào)入座)

image.png

這里引入一個(gè)虛擬類 元類, 元類地址可以通過 objc_getMetaClass 得到.

元類 :

通過實(shí)例對(duì)象(person)的 class 方法可以查看對(duì)象所屬類(Person), 根據(jù)對(duì)象的 isa 可知, 類仍然是一個(gè)對(duì)象, 只是這個(gè)對(duì)象是相對(duì)于元類而言:

對(duì)象 --> 類 == 類對(duì)象 --> 元類

元類什么時(shí)候初始化 ?
運(yùn)用 objc/runtime 的接口 objc_allocateClassPair 動(dòng)態(tài)創(chuàng)建類, 通過源碼可以看到, 該接口在創(chuàng)建類的時(shí)候, 定義了兩個(gè) Class 對(duì)象(結(jié)構(gòu)體) clsmeta, 然后對(duì)兩個(gè)類對(duì)象作空間開辟操作:

Class objc_allocateClassPair(Class superclass, 
                        const char *name, 
                            size_t extraBytes)
{
    Class cls, meta;

    mutex_locker_t lock(runtimeLock);

    // Fail if the class name is in use.
    // Fail if the superclass isn't kosher.
    if (getClass(name) || !verifySuperclass(superclass, true/*rootOK*/)) {
        return nil;
    }

    // Allocate new classes.
    // 僅僅只開辟空間, 開辟一個(gè) objc_class 結(jié)構(gòu)體大小的空間作為返回.
    // _calloc_class(sizeof(objc_class) + extraBytes);
    cls  = alloc_class_for_subclass(superclass, extraBytes);
    meta = alloc_class_for_subclass(superclass, extraBytes);

    // fixme mangle the name if it looks swift-y?
    objc_initializeClassPair_internal(superclass, name, cls, meta);

    return cls;
}

其次會(huì)調(diào)用到 objc_initializeClassPair_internal 函數(shù):

static void objc_initializeClassPair_internal(Class superclass, 
                                         const char *name, 
                                              Class cls, 
                                              Class meta)
{
    runtimeLock.assertLocked();

    class_ro_t *cls_ro_w, *meta_ro_w;
    
    // 開辟類/元類 data / ro
    cls->setData((class_rw_t *)calloc(sizeof(class_rw_t), 1));
    meta->setData((class_rw_t *)calloc(sizeof(class_rw_t), 1));
    cls_ro_w   = (class_ro_t *)calloc(sizeof(class_ro_t), 1);
    meta_ro_w  = (class_ro_t *)calloc(sizeof(class_ro_t), 1);
    cls->data()->ro = cls_ro_w;
    meta->data()->ro = meta_ro_w;

    // Set basic info

    // 查詢 RW_ FLAGS
    cls->data()->flags = RW_CONSTRUCTING | 
                         RW_COPIED_RO | 
                         RW_REALIZED | 
                         RW_REALIZING;
    meta->data()->flags = RW_CONSTRUCTING | 
                          RW_COPIED_RO | 
                          RW_REALIZED | 
                          RW_REALIZING;
    cls->data()->version = 0;
    meta->data()->version = 7;

    cls_ro_w->flags = 0;
    meta_ro_w->flags = RO_META;
    if (!superclass) {
        cls_ro_w->flags |= RO_ROOT;
        meta_ro_w->flags |= RO_ROOT;
    }
    if (superclass) {
        cls_ro_w->instanceStart = superclass->unalignedInstanceSize();
        meta_ro_w->instanceStart = 
                              superclass->ISA()->unalignedInstanceSize();
        cls->setInstanceSize(cls_ro_w->instanceStart);
        meta->setInstanceSize(meta_ro_w->instanceStart);
    } else {
        cls_ro_w->instanceStart = 0;
        meta_ro_w->instanceStart = (uint32_t)sizeof(objc_class);
        cls->setInstanceSize((uint32_t)sizeof(id));  // just an isa
        meta->setInstanceSize(meta_ro_w->instanceStart);
    }

    cls_ro_w->name = strdupIfMutable(name);
    meta_ro_w->name = strdupIfMutable(name);

    cls_ro_w->ivarLayout = &UnsetLayout;
    cls_ro_w->weakIvarLayout = &UnsetLayout;

    meta->chooseClassArrayIndex();
    cls->chooseClassArrayIndex();

// 這里是上面 isa 及 superclass 走位圖的根源!
    // Connect to superclasses and metaclasses
    // 初始化類對(duì)象的 isa 為 meta 類
    cls->initClassIsa(meta);
    if (superclass) {
        // 元類的 isa 指向 元類的元類(根元類)
        meta->initClassIsa(superclass->ISA()->ISA()); 
        cls->superclass = superclass;
        meta->superclass = superclass->ISA(); // 子元類的父類 是 父元類
        addSubclass(superclass, cls);
        addSubclass(superclass->ISA(), meta); // 父類的元類 是 子類元類的父類
    } else {
        meta->initClassIsa(meta); // 根元類指向本身
        cls->superclass = Nil;    // 根類指向 Nil
        meta->superclass = cls;   // 根元類指向根類
        
        // 類關(guān)系 鏈表
        // NSObject->nil
        // 通過 mask 獲取 bits.data() 將 _firstRealizedClass 設(shè)置為 cls
        // cls->data()->nextSiblingClass = _firstRealizedClass;
        // _firstRealizedClass = cls;
        addRootClass(cls);
        
        // NSObject(元類) nextSiblingClass -> NSObject firstSubclass
        // NSObject firstSubclass -> NSObject(元類)
        
        // 將子類的 next 指向父類 first
        // cls(父類)的 firstSubclass 設(shè)置為 meta(子類)
        // subcls->data()->nextSiblingClass = 
        //                            supercls->data()->firstSubclass;
        // supercls->data()->firstSubclass = subcls;
        // NSObject(meta) 的 父類是 NSObject, 即: 根元類的父類是 NSObject;
        addSubclass(cls, meta);
    }

    // 初始化 cache
    cls->cache.initializeToEmpty();
    meta->cache.initializeToEmpty();
    
    // 內(nèi)部遞歸 meta 類, 所以不需要寫   addClassTableEntry(meta);
    addClassTableEntry(cls);
}

這樣, 相信你應(yīng)該知道元類到底是什么了:

  • 元類也是 Class;
  • 子元類的 isa 是 父元類 的 元類, 即: 根元類;
  • 根元類的 isa 指向自己;
  • 根類的父類是 nil;
  • 子類的元類的父類是父類的元類;
  • 根元類的父類是根類;

isa 作用

那么 isa 的作用是什么呢?

  • 查找類的實(shí)現(xiàn):
    在動(dòng)態(tài)創(chuàng)建類時(shí)還需要調(diào)用另外一個(gè)接口: objc_registerClassPair(Class cls):
objc_registerClassPair(Class cls) {
    // 改變標(biāo)記值 正在創(chuàng)建 -> 已創(chuàng)建
    cls->ISA()->changeInfo(RW_CONSTRUCTED, \
                           RW_CONSTRUCTING | RW_REALIZING);
    cls->changeInfo(RW_CONSTRUCTED, 
                           RW_CONSTRUCTING | RW_REALIZING);

    // Add to named class table.
    addNamedClass(cls, cls->data()->ro->name);
}

該接口會(huì)將注冊(cè)的 clskey-value 的形式添加到 gdb_objc_realized_classes表中 :

// 添加類實(shí)現(xiàn)到 hash 表中
static void addNamedClass(Class cls, 
                     const char *name, 
                          Class replacing = nil) {
    // 插入到 NXMapTable hash 表中, 以 name 作為 key, cls 作為 value 保存
    NXMapInsert(gdb_objc_realized_classes, name, cls);
}

gdb_objc_realized_classes 表在 load_images(dyld 注冊(cè)的回調(diào)) -> read_images 第一次的時(shí)候就會(huì)初始化, 在查找類實(shí)現(xiàn)的時(shí)候會(huì)通過 name(類名) 從該表中進(jìn)行查找, 即 getClass(const char *name):

static Class getClass(const char *name) {
    runtimeLock.assertLocked();
    // Try name as-is
    Class result = getClass_impl(name);
    if (result) return result;
}
static Class getClass_impl(const char *name)
{
    runtimeLock.assertLocked();
    // Try runtime-allocated table
    Class result = (Class)NXMapGet(gdb_objc_realized_classes, name);
    if (result) return result;

    // Try table from dyld shared cache
    return getPreoptimizedClass(name);
}

因?yàn)槭谦@取類的實(shí)現(xiàn), 而獲取 類實(shí)現(xiàn) 在 objc/runtime 中, 即: objc_getClass:

Class objc_getClass(const char *aClassName) {
    return look_up_class(aClassName, NO, YES);
}
look_up_class(const char *name, 
              bool includeUnconnected __attribute__((unused)), 
              bool includeClassHandler __attribute__((unused))) {
 
    Class result;
    bool unrealized;
    {
        mutex_locker_t lock(runtimeLock);
        result = getClass(name);
        unrealized = result  &&  !result->isRealized();
    }
    if (unrealized) {
        mutex_locker_t lock(runtimeLock);
        realizeClass(result);
    }
    return result;
}

可以看到, 在 look_up_class 函數(shù)中會(huì)調(diào)用 getClass 接口去獲取類的實(shí)現(xiàn)!

  • 方法查找:
    在調(diào)用類方法的時(shí)候, receiver 由編譯器編譯成 objc_getClass(類對(duì)象) 獲取類的實(shí)現(xiàn)(通過匯編取 isa 找元類, 其實(shí)是找類對(duì)象的實(shí)現(xiàn)), 然后在元類中找方法的 IMP;
    而調(diào)用對(duì)象方法時(shí), 則會(huì)在通過 isa 獲取對(duì)象的 class(即類對(duì)象, 其實(shí)是找對(duì)象的實(shí)現(xiàn)) 中查找方法的IMP;
.macro GetClassFromIsa_p16 /* src */
#if SUPPORT_INDEXED_ISA
    // Indexed isa
    mov p16, $0         // optimistically set dst = src
    tbz p16, #ISA_INDEX_IS_NPI_BIT, 1f  // done if not non-pointer isa
    // isa in p16 is indexed
    adrp    x10, _objc_indexed_classes@PAGE
    add x10, x10, _objc_indexed_classes@PAGEOFF
    ubfx    p16, p16, #ISA_INDEX_SHIFT, #ISA_INDEX_BITS  // extract index
    ldr p16, [x10, p16, UXTP #PTRSHIFT] // load class from array
1:
#elif __LP64__
    // 64-bit packed isa
    and p16, $0, #ISA_MASK   
#else
    // 32-bit raw isa
    mov p16, $0
#endif
.endmacro

至此, 可以了解到, isa 的作用之一是查找類的實(shí)現(xiàn)(class_impl), 而且, 在調(diào)用方法的時(shí)候并沒有區(qū)分類方法/對(duì)象方法, 只是通過 isa 去類對(duì)象中查找方法的 IMP.

cache

首先看下 cache_t 結(jié)構(gòu):


image.png

mask:緩存 bucket 的總數(shù).
occupied:目前實(shí)際占用的緩存 bucket 的個(gè)數(shù)巴比。
buckets:hash 表钳枕,用來緩存方法,bucket_t 類型,包含 key(sel 方法編號(hào)) 以及方法實(shí)現(xiàn) IMP。

看該結(jié)構(gòu)體提供方法就可以知道, cache_t 是以 hash 表的方式存儲(chǔ)了方法的 IMP,
將 sel 方法編號(hào)轉(zhuǎn)為 cache_key_t 類型即 uintptr_t 作為 key 存儲(chǔ).

typedef uintptr_t cache_key_t;

cache_key_t getKey(SEL sel) 
{
    assert(sel);
    return (cache_key_t)sel;
}

在調(diào)用方法時(shí), objc_msgSend 會(huì)先找 cache, 指的就是這里的方法緩存列表.


image.png

bits

bits 結(jié)構(gòu):


image.png

可以看到 bits 中 data() 主要存儲(chǔ)了 方法/屬性/協(xié)議 列表, 并且記錄當(dāng)前類的第一個(gè)子類, 以及下一個(gè)類, flags 標(biāo)記當(dāng)前類狀態(tài). version 標(biāo)記當(dāng)前類類型, 如:cls version = 0, meta version = 7,
其他只讀屬性保存在屬性 ro 結(jié)構(gòu)體中.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市嵌屎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胳岂,老刑警劉巖编整,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異乳丰,居然都是意外死亡掌测,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門产园,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汞斧,“玉大人,你說我怎么就攤上這事什燕≌忱眨” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵屎即,是天一觀的道長庙睡。 經(jīng)常有香客問我事富,道長,這世上最難降的妖魔是什么乘陪? 我笑而不...
    開封第一講書人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任统台,我火速辦了婚禮,結(jié)果婚禮上啡邑,老公的妹妹穿的比我還像新娘贱勃。我一直安慰自己,他們只是感情好谤逼,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開白布贵扰。 她就那樣靜靜地躺著,像睡著了一般流部。 火紅的嫁衣襯著肌膚如雪戚绕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,328評(píng)論 1 310
  • 那天贵涵,我揣著相機(jī)與錄音列肢,去河邊找鬼。 笑死宾茂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拴还。 我是一名探鬼主播跨晴,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼片林!你這毒婦竟也來了端盆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤费封,失蹤者是張志新(化名)和其女友劉穎焕妙,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弓摘,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡焚鹊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了韧献。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片末患。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖锤窑,靈堂內(nèi)的尸體忽然破棺而出璧针,到底是詐尸還是另有隱情,我是刑警寧澤渊啰,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布探橱,位于F島的核電站申屹,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏隧膏。R本人自食惡果不足惜独柑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望私植。 院中可真熱鬧忌栅,春花似錦、人聲如沸曲稼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贫悄。三九已至瑞驱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間窄坦,已是汗流浹背唤反。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鸭津,地道東北人彤侍。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像逆趋,于是被迫代替她去往敵國和親盏阶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359