IOS底層(九): 類相關(guān): 類結(jié)構(gòu)分析

OC底層源碼/原理合集

建議先看下
IOS底層(三): alloc相關(guān)1.初探 alloc, init, new源碼分析
IOS底層(八): alloc相關(guān): isa與類關(guān)聯(lián)源碼分析

首先看個例子:

exp

XXX & 0x00007ffffffffff8ULL這塊先解釋下, 之前我們講過ISA源碼

#   define ISA_MASK        0x00007ffffffffff8ULL  // x86_64下

inline Class 
objc_object::ISA() 
{
    ASSERT(!isTaggedPointer()); 
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    return (Class)(isa.bits & ISA_MASK);
#endif
}

這里 isa.bits & ISA_MASK 是返回類信息

我們分解看下上面例子

  • x/4gx test讀一下內(nèi)存段, 第一個是isa, 我們p/x一下 isa & ISA_MASK, 可看到有0x0000000100008388, 我們接下來po一下, po 0x0000000100008388 沒問題返回自定義類SATest
    (① x/4gx 讀內(nèi)存段, 打印內(nèi)存情況, 第一個是isa指針地址
    ② p/x isa & ISA_MASK是獲取類信息, 返回的是類的指針地址, 此時類是SATest
    ③ po 是打印類信息 (都是lldb調(diào)試命令))

  • 既然是類信息, 我們再做一次x/4gxp/x isa & ISA_MASKpo可看到po 0x0000000100008360(類isa指針地址)也為SATest

  • 既然是類信息, 我們再再做一次x/4gxp/x isa & ISA_MASKpo可看到0x000000010036a140(類isa指針地址)NSObject

其實第二次時候就應(yīng)該有疑問, 為什么還是SATest, 而第三次是NSObject

第二次中 0x0000000100008360是之前isa中獲取的isa的指針地址, 即SATest類的類, 在Apple中我們稱SATest類的類元類, 之后的NSObject也稱為元類, 不過由于它源自根類, 所以也可以成為根元類

(下面圖片是新走一遍的結(jié)果, 之前不小心給關(guān)了, 打印地址可能有點(diǎn)差別, 原理不變)


元類探索

元類

  1. 首先了解一點(diǎn), 對象isa指向的, 類也是一個對象( 所以有個流傳, 萬物皆對象:) ), 這個對象我們一般稱為類對象, 其isa位域指向蘋果定義的元類

  2. 元類系統(tǒng)給的, 其定義創(chuàng)建都是由編譯器完成, 歸屬來自于元類

3.元類類對象, 每個都有一個獨(dú)一無二的元類用來存儲類方法相關(guān)信息

4.元類本身是沒有名稱的, 由于與關(guān)聯(lián), 所以使用了同類名一樣的名稱

由上面例子也可以得到關(guān)系
對象元類NSObject, NSObject元類指向自身

總結(jié)
元類走位圖
  • 對象isa(也稱類對象)
  • isa 指向元類
  • 元類isa 指向NSObject (根元類)
  • NSObject的isa指向本身

擴(kuò)展個問題, 剛才看到自定義的一個類, 有元類有根元類依次循環(huán), 那么豈不是, 創(chuàng)建一個類, 系統(tǒng)會自動幫我們創(chuàng)建多個NSObject?

我們可以這樣驗證下

        Class cls1 = [SATest class];
        Class cls2 = [SATest alloc].class;
        Class cls3 = object_getClass([SATest alloc]);
        NSLog(@"cls1: %p", cls1);
        NSLog(@"cls2: %p", cls2);
        NSLog(@"cls3: %p", cls3);
驗證類1

可看到打印地址只有一個, 所以NSObject只有一份, 或者說NSObject(根元類)在內(nèi)存中只有一份。(類的信息在內(nèi)存中永遠(yuǎn)只存在一份, 類對象只有一份)

或者通過lldb驗證也可以, 看下下面圖片即可(根元類只是指向自己)

驗證類2

isa走勢關(guān)系圖

isa走勢關(guān)系圖

(留意下虛線是isa, 實線是superclass)

  • isa走勢:

實例對象(Instance) isa指向 → Class(類) isa 指向 → meta(元類) isa 指向 → root (meta 根元類), 而root (meta 根元類) isa 指向 → NSObject

父類實例對象也是一樣走勢

自定義類打印
NSObject打印

如果是類NSObject, 會省去一個元類指向

  • superclass走勢:

子類(SubClass) 繼承 → 父類(SuperClass) →繼承 → 根類(Root Class)
根類即NSObject無繼承

子元類(meta SubClass) 繼承 → 父元類(meta SuperClass) →繼承 → 根元類(meta Root Class) 繼承 → NSObject

  • 實例對象沒有繼承關(guān)系, 類才會有繼承關(guān)系, NSObject繼承nil
isa走勢關(guān)系圖標(biāo)注

objc_class & objc_object

既然提到了類和對象, 我們看下objc_classobjc_object源碼


1. typedef struct objc_class *Class;

2. struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */

// objc-runtime-new.h
3. struct objc_class : objc_object {
  objc_class(const objc_class&) = delete;
  objc_class(objc_class&&) = delete;
  void operator=(const objc_class&) = delete;
  void operator=(objc_class&&) = delete;
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    Class getSuperclass() const {
#if __has_feature(ptrauth_calls)
// objc.h

1. struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

// objc-private.h

2. struct objc_object {
private:
    isa_t isa;

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA(bool authenticated = false);

    // rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
    Class rawISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();
    
    uintptr_t isaBits() const;

    // initIsa() should be used to init the isa of new objects only.
    // If this object already has an isa, use changeIsa() for correctness.
    // initInstanceIsa(): objects with no custom RR/AWZ
    // initClassIsa(): class objects
    // initProtocolIsa(): protocol objects
    // initIsa(): other objects
    void initIsa(Class cls /*nonpointer=false*/);
    void initClassIsa(Class cls /*nonpointer=maybe*/);
    void initProtocolIsa(Class cls /*nonpointer=maybe*/);
    void initInstanceIsa(Class cls, bool hasCxxDtor);

    // changeIsa() should be used to change the isa of existing objects.
    // If this is a new object, use initIsa() for performance.
    Class changeIsa(Class newCls);
...

首先可以看到objc_classobjc_object都有一個 isa 指針, 這個isa來自于objc_object(struct objc_object { Class _Nonnull isa OBJC_ISA_AVAILABILITY; };), 且在新版objc-runtime-new.h里面可看到objc_class是繼承于objc_object

總結(jié):

  • Class底層是objc_class (typedef struct objc_class *Class; 結(jié)構(gòu)體類型 ), 所有類都是以objc_class為模板繼承過來的。

  • objc_object 與對象的關(guān)系是 繼承關(guān)系, 所有的對象都是以objc_object為模板繼承過來的, (但是真正到底層的是一個objc_object(C/C++)結(jié)構(gòu)體類型)

  • 結(jié)構(gòu)體類型objc_class 繼承自objc_object, 其中objc_object(結(jié)構(gòu)體), 因為是繼承關(guān)系且有一個isa屬性, 所以objc_class也擁有了isa屬性 ( 其實的對象, , 元類 都有isa屬性)

  • NSObject中的isa在底層是由Class 定義的, 其中class的底層編碼來自 objc_class類型, 所以NSObject也擁有了isa屬性

  • NSObject 是一個類,用它初始化一個實例對象objc茎刚,objc 滿足 objc_object 的特性(即有isa屬性),主要是因為isa 是由 NSObject 從objc_class繼承過來的秒咐,而objc_class繼承自objc_object股囊,objc_object 有isa屬性潮饱。所以對象都有一個 isaisa表示指向豺型,來自于當(dāng)前的objc_object(對象, 類, 元類都有isa)

  • objc_object根對象仲智,所有的對象都有這樣一個特性 objc_object,即擁有isa屬性

  • 在結(jié)構(gòu)層面可以通俗的理解為上層OC 與 底層的對接:
    ① 下層是通過結(jié)構(gòu)體定義的 模板姻氨,例如 objc_class、objc_object
    ② 上層是通過底層的模板創(chuàng)建的 一些類型剪验,例如CATest

objc_class肴焊、objc_object、isa功戚、object娶眷、NSObject關(guān)系圖

類結(jié)構(gòu)分析

首先還是先看一個例子

例子1: 普通指針
普通指針

定義2個變量a, b = 10, 打印兩個變量值以及內(nèi)存地址

普通指針地址指向
  • a 和 b 為變量都指向10, 10是系統(tǒng)開辟的固定內(nèi)存空間, 其他需要10的值的變量都可以指向內(nèi)存固定生成的10

  • a 和 b 地址不一樣, 這是一種拷貝, 屬于值拷貝, 也成深拷貝, 可發(fā)現(xiàn)a, b地址相差 4 個字節(jié),這取決于a啸臀、b的類型

例子2: 對象指針
對象指針
  • &p1/&p2 是二級指針, 指向?qū)ο蟮闹羔樀刂?0x7ffeefbff478, 0x7ffeefbff480 為對象指針)

  • p1/p2 是一級指針, 指向的 [SATest alloc] 開辟空間的內(nèi)存地址

  • SATest[SATest alloc]創(chuàng)建內(nèi)存空間, [SATest alloc]開辟空間的 isa指向SATest

對象指針地址指向
例子3: 數(shù)組指針
數(shù)組指針
  • &arr == &arr[0] == 首地址, 其實他都是取的首地址, 數(shù)組地址其實就是數(shù)組第一個元素地址即數(shù)組名為首地址

  • &arr[0]與%arr[1]相差4字節(jié), 取決于數(shù)據(jù)類型

  • 數(shù)組類型指針可以通過首地址+偏移量得到其他元素(偏移量為數(shù)組下標(biāo))

  • 移動的字節(jié)數(shù) 等于 偏移量 * 數(shù)據(jù)類型字節(jié)數(shù), 這個根據(jù)&arr[0], &arr[1]看出, 兩者相差4

有了上面的概念, 便于我們理解之后的探索


bits探索

再回頭看下objc_class源碼(在 objc-runtime-new.h)

objc-runtime-new.h

struct objc_class : objc_object {
  objc_class(const objc_class&) = delete;
  objc_class(objc_class&&) = delete;
  void operator=(const objc_class&) = delete;
  void operator=(objc_class&&) = delete;
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

...
    class_rw_t *data() const {
        return bits.data();
    }

...
}

拋去那些刪除的, ISA8字節(jié), superclassClass類型占8字節(jié)(這里的Class是由objc_object定義的届宠,是一個指針), bits 只有首地址經(jīng)過上面3個屬性的內(nèi)存大小總和的平移,才能獲取到bits(bits有所以我們想要的信息), 那么我們接下來看下cache

struct cache_t {
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask;
    union {
        struct {
            explicit_atomic<mask_t>    _maybeMask;
#if __LP64__
            uint16_t                   _flags;
#endif
            uint16_t                   _occupied;
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache;
    };
...
}

先介紹幾個定義

  • uintptr_t定義 typedef unsigned long uintptr_t;, long 8字節(jié)
  • mask_t定義typedef uint32_t mask_t;, typedef unsigned int uint32_t;, int類型占4字節(jié)
  • uint32_t定義 typedef unsigned int uint32_t;, int類型占4字節(jié)
  • uint16_t定義typedef unsigned short uint16_t; short類型占2字節(jié)

拆開看一下

占8字節(jié)
explicit_atomic<uintptr_t> _bucketsAndMaybeMask; // long類型占8字節(jié)

占8字節(jié)
        struct {
            explicit_atomic<mask_t>    _maybeMask; // int 4字節(jié)
#if __LP64__
            uint16_t                   _flags; // short 占2字節(jié)
#endif
            uint16_t                   _occupied; // short 占2字節(jié)
        };

接下來看下 originalPreoptCache

看下 preopt_cache_t
explicit_atomic<preopt_cache_t *> _originalPreoptCache;

/* dyld_shared_cache_builder and obj-C agree on these definitions */
struct preopt_cache_entry_t {
    uint32_t sel_offs;
    uint32_t imp_offs;
};

/* dyld_shared_cache_builder and obj-C agree on these definitions */
struct preopt_cache_t {
    int32_t  fallback_class_offset;
    union {
        struct {
            uint16_t shift       :  5;
            uint16_t mask        : 11;
        };
        uint16_t hash_params;
    };
    uint16_t occupied    : 14;
    uint16_t has_inlines :  1;
    uint16_t bit_one     :  1;
    preopt_cache_entry_t entries[];

    inline int capacity() const {
        return mask + 1;
    }
};

最后可計算出cache類的內(nèi)存大小16字節(jié), 那么總共需要偏移 8 + 8 + 16 = 32個字節(jié)乘粒。即首地址平移32位得到 bits(bit主要儲存相關(guān)類信息)

創(chuàng)建2個類, SAPerson繼承NSObject, SAStudent繼承SAPerson

查看bit信息
  • p/x SAPerson.class 獲取 SAPerson類首地址得到 0x0000000100008260 SAPerson

  • x/4gx 0x0000000100008260 打印首地址isa指針的內(nèi)存信息,

  • 將得到0x100008260平移32位得0x100008280(此處為16進(jìn)制, 逢16進(jìn)1, 32則相當(dāng)于進(jìn)2個16即 8260變?yōu)?code>8280)

  • bits是這個類型class_data_bits_t bits, 我們這邊為了讀取數(shù)據(jù)轉(zhuǎn)一下

  • p $1 -> data通過bits地址得到bits數(shù)據(jù), 這里的data看源碼可知是class_rw_t類型的豌注。留意下指針函數(shù)用->((XXXX *)結(jié)構(gòu)), 結(jié)構(gòu)體用.

 class_rw_t *data() const {
        return bits.data();
}
  • p *$2打印bits中數(shù)據(jù)信息

由于bits里面數(shù)據(jù)類型是class_rw_t, 為了方便探索類里面的屬性, 方法等, 我們接下來看一下class_rw_t源碼

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint16_t witness;
#if SUPPORT_INDEXED_ISA
    uint16_t index;
#endif

    explicit_atomic<uintptr_t> ro_or_rw_ext;

    Class firstSubclass;
    Class nextSiblingClass;
...
    const class_ro_t *ro() const {
        auto v = get_ro_or_rwe();
        if (slowpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
        }
        return v.get<const class_ro_t *>(&ro_or_rw_ext);
    }
    const method_array_t methods() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
        } else {
            return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
        }
    }

    const property_array_t properties() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
        } else {
            return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
        }
    }

    const protocol_array_t protocols() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
        } else {
            return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
        }
    }
};

通過查看class_rw_t源碼可看到, 首先class_rw_t也是個結(jié)構(gòu)體類型, 結(jié)構(gòu)體中有提供相應(yīng)的方法去獲取 properties 屬性列表method 方法列表灯萍、protocols協(xié)議列表

那么我們在SAPerson定義幾個屬性, 方法

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface SAPerson : NSObject {
    NSString *hobby;
}

@property (nonatomic, copy) NSString *sa_name;
@property (nonatomic, assign) NSInteger sa_age;

- (void)sayYes;
+ (void)sayNo;


@end
屬性列表

bits 數(shù)據(jù)信息 $3在之前的例子我上面已經(jīng)講過了, 我們從讀bits數(shù)據(jù)信息$3之后開始

  • p $3.properties()獲得的屬性列表的list結(jié)構(gòu), 其中list中的ptr就是屬性數(shù)組的參數(shù)指針地址轧铁。(p $3.properties()命令中的propertoes方法是由class_rw_t提供的, 方法中返回的實際類型為property_array_t)

  • p *$4.list.ptr讀一下指針地址, 可獲取內(nèi)存信息, count = 2, 也符合我們建的2個屬性

  • p $5.get(0)可獲取到sa_name對應(yīng)屬性(property_t) $6 = (name = "sa_name", attributes = "T@\"NSString\",C,N,V_sa_name")

  • p $5.get(1)可獲取到sa_age屬性(property_t) $7 = (name = "sa_age", attributes = "Tq,N,V_sa_age")

  • p $6.get(2)數(shù)組越界, 因為我們只建立了2個屬性

我們接下來看下方法


方法列表
  • p $3.methods()獲得的方法列表的list結(jié)構(gòu), 接下來跟讀屬性類型, 依次讀取指針地址, 讀取列表對應(yīng)項

  • p $5.get(0).name讀取出方法名

  • .cxx_destruct由于底層是C++, 系統(tǒng)默認(rèn)添加的方法

  • 可以看到, 有自定義的方法sayYes, 系統(tǒng)自動生成的2個屬性的set, get方法, 方法列表也有數(shù)組越界, 例如count = 6, p $5.get(6).name讀的時候也能看見數(shù)組越界報錯

當(dāng)然你要讀協(xié)議列表的list結(jié)構(gòu), 那里就p $3.protocols()即可

其實到這里我們會有疑問, 屬性, 方法``協(xié)議打印出來了沒問題, 但是成員變量hobby類方法sayNo并沒有打印出來。我們再返回看一下struct class_rw_t , 里面除了methods旦棉、properties齿风、protocols,還有一個ro方法 constclass_ro_t *ro(), 看下ro`底層

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    union {
        const uint8_t * ivarLayout;
        Class nonMetaclass;
    };

    explicit_atomic<const char *> name;
    // With ptrauth, this is signed if it points to a small list, but
    // may be unsigned if it points to a big list.
    void *baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

  ...
};

可看到const ivar_list_t * ivars;, 有一個ivars屬性, 我們仿照下上面也讀一下ro

ro

可看到成員變量hoppy儲存在ivar_list_t里面

總結(jié)

  • 通過XXXX {}定義的成員變量绑洛,會存儲在類的bits屬性中救斑,通過bits --> data() -->ro() --> ivars獲取成員變量列表,除了包括成員變量真屯,還包括屬性的成員變量

  • 通過@property定義的屬性脸候,也會存儲在bits屬性中,通過bits --> data() --> properties() --> list獲取屬性列表讨跟,其中只包含property屬性

接下來我們看下類方法存在哪里


類方法
  • x/4g SAPerson.class獲取類的內(nèi)存信息, 以4片段打印

  • p/x 0x00000001000083b8 & 0x00007ffffffffff8UL 獲取元類的首地址

  • p (class_data_bits_t *)0x00000001000083d8轉(zhuǎn)成class_data_bits_t型便于我們獲取bits信息, 同時別忘了平移32位, 元類的首地址平移32位得到bits信息

  • p $2->data()通過元類地址獲取bits信息

  • p *$3打印bits數(shù)據(jù)

  • p $4.methods()獲取元類bits中的方法數(shù)組

  • p $5.list.ptr/p *$6(直接p *$5.list.ptr也可以) 獲取元類方法數(shù)組中的方法列表

  • p $7.get(0).name讀出方法名, 可看到類方法是儲存在元類中的

總結(jié)

  • 類的實例方法儲存在類的bits屬性中纪他。 系統(tǒng)自動生成自定義屬性@propertyset, get方法, 也是存在這里。

  • 類的類方法儲存在元類的bits屬性中晾匠。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末茶袒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子凉馆,更是在濱河造成了極大的恐慌薪寓,老刑警劉巖亡资,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異向叉,居然都是意外死亡锥腻,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門母谎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瘦黑,“玉大人,你說我怎么就攤上這事奇唤⌒页猓” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵咬扇,是天一觀的道長甲葬。 經(jīng)常有香客問我,道長懈贺,這世上最難降的妖魔是什么经窖? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮梭灿,結(jié)果婚禮上画侣,老公的妹妹穿的比我還像新娘。我一直安慰自己胎源,他們只是感情好棉钧,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著涕蚤,像睡著了一般宪卿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上万栅,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天佑钾,我揣著相機(jī)與錄音,去河邊找鬼烦粒。 笑死休溶,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的扰她。 我是一名探鬼主播兽掰,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼徒役!你這毒婦竟也來了孽尽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤忧勿,失蹤者是張志新(化名)和其女友劉穎杉女,沒想到半個月后瞻讽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡熏挎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年速勇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坎拐。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡烦磁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出哼勇,到底是詐尸還是另有隱情个初,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布猴蹂,位于F島的核電站,受9級特大地震影響楣嘁,放射性物質(zhì)發(fā)生泄漏磅轻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一逐虚、第九天 我趴在偏房一處隱蔽的房頂上張望聋溜。 院中可真熱鬧,春花似錦叭爱、人聲如沸撮躁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽把曼。三九已至,卻和暖如春漓穿,著一層夾襖步出監(jiān)牢的瞬間嗤军,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工晃危, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留叙赚,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓僚饭,卻偏偏與公主長得像震叮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鳍鸵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348

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