iOS 底層探索 - 類

iOS 底層探索 - 類.png

iOS 底層探索系列

我們?cè)谇懊嫣剿髁?iOS 中的對(duì)象原理厌衔,面向?qū)ο缶幊讨杏幸痪涿?

萬物皆對(duì)象

那么對(duì)象又是從哪來的呢获三?有過面向?qū)ο缶幊袒A(chǔ)的同學(xué)肯定都知道是類派生出對(duì)象的,那么今天我們就一起來探索一下類的底層原理吧。

一双藕、iOS 中的類到底是什么?

我們?cè)谌粘i_發(fā)中大多數(shù)情況都是從 NSObject 這個(gè)基類來派生出我們需要的類遂鹊。那么在 OC 底層栋艳,我們的類 Class 到底被編譯成什么樣子了呢飞袋?

我們新建一個(gè) macOS 控制臺(tái)項(xiàng)目戳气,然后新建一個(gè) Animal 類出來。

// Animal.h
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Animal : NSObject

@end

NS_ASSUME_NONNULL_END

// Animal.m
@implementation Animal

@end

// main.m
#import <Foundation/Foundation.h>
#import "Animal.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Animal *animal = [[Animal alloc] init];
        NSLog(@"%p", animal);
    }
    return 0;
}

我們?cè)诮K端執(zhí)行 clang 命令:

clang -rewrite-objc main.m -o main.cpp

這個(gè)命令是將我們的 main.m 重寫成 main.cpp巧鸭,我們打開這個(gè)文件搜索 Animal:

image

我們發(fā)現(xiàn)有多個(gè)地方都出現(xiàn)了 Animal:

// 1
typedef struct objc_object Animal;

// 2
struct Animal_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
};

// 3
objc_getClass("Animal")

我們先全局搜索第一個(gè) typedef struct objc_object瓶您,發(fā)現(xiàn)有 843 個(gè)結(jié)果

image

我們通過 Command + G 快捷鍵快速翻閱一下,最終在 7626 行找到了 Class 的定義:

typedef struct objc_class *Class;

由這行代碼我們可以得出一個(gè)結(jié)論纲仍,Class 類型在底層是一個(gè)結(jié)構(gòu)體類型的指針呀袱,這個(gè)結(jié)構(gòu)體類型為 objc_class
再搜索 typedef struct objc_class 發(fā)現(xiàn)搜不出來了郑叠,這個(gè)時(shí)候我們需要在 objc4-756 源碼中進(jìn)行探索了夜赵。

我們?cè)?objc4-756 源碼中直接搜索 struct objc_class ,然后定位到 objc-runtime-new.h 文件

struct objc_class : objc_object {
    // 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() { 
        return bits.data();
    }
}

看到這里锻拘,細(xì)心的讀者可能會(huì)發(fā)現(xiàn)油吭,我們?cè)谇懊嫣剿鲗?duì)象原理中遇到的 objc_object 再次出現(xiàn)了,并且這次是作為 objc_class 的父類署拟。這里再次引用那句經(jīng)典名言 萬物皆對(duì)象,也就是說類其實(shí)也是一種對(duì)象歌豺。

由此推穷,我們可以簡(jiǎn)單總結(jié)一下類和對(duì)象在 COC 中分別的定義

C OC
objc_object NSObject
objc_class NSObject(Class)

二、類的結(jié)構(gòu)是什么樣的呢类咧?

通過上面的探索馒铃,我們已經(jīng)知道了類本質(zhì)上也是對(duì)象,而日常開發(fā)中常見的成員變量痕惋、屬性区宇、方法、協(xié)議等都是在類里面存在的值戳,那么我們是不是可以猜想在 iOS 底層议谷,類其實(shí)就存儲(chǔ)了這些內(nèi)容呢?

我們可以通過分析源碼來驗(yàn)證我們的猜想堕虹。

從上一節(jié)中 objc_class 的定義處卧晓,我們可以梳理出 Class 中的 4 個(gè)屬性

  • isa 指針
  • superclass 指針
  • cache
  • bits

需要值得注意的是,這里的 isa 指針在這里是隱藏屬性.

2.1 isa 指針

首先是 isa 指針赴捞,我們之前已經(jīng)探索過了逼裆,在對(duì)象初始化的時(shí)候,通過 isa 可以讓對(duì)象和類關(guān)聯(lián)赦政,這一點(diǎn)很好理解胜宇,可是為什么在類結(jié)構(gòu)里面還會(huì)有 isa 呢?看過上一篇文章的同學(xué)肯定知道這個(gè)問題的答案了。沒錯(cuò)桐愉,就是元類封寞。我們的對(duì)象和類關(guān)聯(lián)起來需要 isa,同樣的仅财,類和元類之間關(guān)聯(lián)也需要 isa狈究。

2.2 superclass 指針

顧名思義,superclass 指針表明當(dāng)前類指向的是哪個(gè)父類盏求。一般來說抖锥,類的根父類基本上都是 NSObject 類。根元類的父類也是 NSObject 類碎罚。

2.3 cache 緩存

cache 的數(shù)據(jù)結(jié)構(gòu)為 cache_t磅废,其定義如下:

struct cache_t {
    struct bucket_t *_buckets;
    mask_t _mask;
    mask_t _occupied;
    
    ...省略代碼...
}

類的緩存里面存放的是什么呢?是屬性荆烈?是實(shí)例變量拯勉?還是方法?我們可以通過閱讀 objc-cache.mm 源文件來解答這個(gè)問題憔购。

  • objc-cache.m
  • Method cache management
  • Cache flushing
  • Cache garbage collection
  • Cache instrumentation
  • Dedicated allocator for large caches

上面是 objc-cache.mm 源文件的注釋信息宫峦,我們可以看到 Method cache management 的出現(xiàn),翻譯過來就是方法緩存管理玫鸟。那么是不是就是說 cache 屬性就是緩存的方法呢导绷?而 OC 中的方法我們現(xiàn)在還沒有進(jìn)行探索,先假設(shè)我們已經(jīng)掌握了相關(guān)的底層原理屎飘,這里先簡(jiǎn)單提一下妥曲。

我們?cè)陬惱锩婢帉懙姆椒ǎ诘讓悠鋵?shí)是以 SEL + IMP 的形式存在钦购。SEL 就是方法的選擇器檐盟,而 IMP 則是具體的方法實(shí)現(xiàn)。這里可以以書籍的目錄以及內(nèi)容來類比押桃,我們查找一篇文章的時(shí)候葵萎,需要先知道其標(biāo)題(SEL),然后在目錄中看有沒有對(duì)應(yīng)的標(biāo)題怨规,如果有那么就翻到對(duì)應(yīng)的頁陌宿,最后我們就找到了我們想要的內(nèi)容。當(dāng)然波丰,iOS 中方法要比書籍的例子復(fù)雜一些壳坪,不過暫時(shí)可以這么簡(jiǎn)單的理解,后面我們會(huì)深入方法的底層進(jìn)行探索掰烟。

2.4 bits 屬性

bits 的數(shù)據(jù)結(jié)構(gòu)類型是 class_data_bits_t爽蝴,同時(shí)也是一個(gè)結(jié)構(gòu)體類型沐批。而我們閱讀 objc_class 源碼的時(shí)候,會(huì)發(fā)現(xiàn)很多地方都有 bits 的身影蝎亚,比如:

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

bool hasCustomRR() {
    return ! bits.hasDefaultRR();
}    

bool canAllocFast() {
    assert(!isFuture());
    return bits.canAllocFast();
}

這里值得我們注意的是九孩,objc_classdata() 方法其實(shí)是返回的 bitsdata() 方法,而通過這個(gè) data() 方法发框,我們發(fā)現(xiàn)諸如類的字節(jié)對(duì)齊躺彬、ARC、元類等特性都有 data() 的出現(xiàn)梅惯,這間接說明 bits 屬性其實(shí)是個(gè)大容器宪拥,有關(guān)于內(nèi)存管理、C++ 析構(gòu)等內(nèi)容在其中有定義铣减。

這里我們會(huì)遇到一個(gè)十分重要的知識(shí)點(diǎn): class_rw_t她君,data() 方法的返回值就是 class_rw_t 類型的指針對(duì)象。我們?cè)诒疚暮竺鏁?huì)重點(diǎn)介紹葫哗。

三缔刹、類的屬性存在哪?

上一節(jié)我們對(duì) OC 中類結(jié)構(gòu)有了基本的了解劣针,但是我們平時(shí)最常打交道的內(nèi)容-屬性校镐,我們還不知道它究竟是存在哪個(gè)地方。接下來我們要做一件事情酿秸,就是在 objc4-756 的源碼中新建一個(gè) Target灭翔,為什么不直接用上面的 macOS 命令行項(xiàng)目呢?因?yàn)槲覀円_始結(jié)合 LLDB 打印一些類的內(nèi)部信息辣苏,所以只能是新建一個(gè)依靠于 objc4-756 源碼 projecttarget 出來。同樣的哄褒,我們還是選擇 macOS 的命令行作為我們的 target稀蟋。

接著我們新建一個(gè)類 Person,然后添加一些實(shí)例變量和屬性出來呐赡。

// Person.h
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject
{
    NSString *hobby;
}
@property (nonatomic, copy) NSString *nickName;
@end

NS_ASSUME_NONNULL_END

// main.m
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Person *p = [[Person alloc] init];
        Class pClass = object_getClass(p);
        NSLog(@"%s", p);
    }
    return 0;
}

我們打一個(gè)斷點(diǎn)到 main.m 文件中的 NSLog 語句處退客,然后運(yùn)行剛才新建的 target

target 跑起來之后链嘀,我們?cè)诳刂婆_(tái)先打印輸出一下 pClass 的內(nèi)容:

image

3.1 類的內(nèi)存結(jié)構(gòu)

我們這個(gè)時(shí)候需要借助指針平移來探索萌狂,而對(duì)于類的內(nèi)存結(jié)構(gòu)我們先看下面這張表格:

類的內(nèi)存結(jié)構(gòu) 大小(字節(jié))
isa 8
superclass 8
cache 16

前兩個(gè)大小很好理解,因?yàn)?isasuperclass 都是結(jié)構(gòu)體指針怀泊,而在 arm64 環(huán)境下茫藏,一個(gè)結(jié)構(gòu)體指針的內(nèi)存占用大小為 8 字節(jié)。而第三個(gè)屬性 cache 則需要我們進(jìn)行抽絲剝繭了霹琼。

cache_t cache;

struct cache_t {
    struct bucket_t *_buckets; // 8
    mask_t _mask;  // 4
    mask_t _occupied; // 4
}

typedef uint32_t mask_t; 

從上面的代碼我們可以看出务傲,cache 屬性其實(shí)是 cache_t 類型的結(jié)構(gòu)體凉当,其內(nèi)部有一個(gè) 8 字節(jié)的結(jié)構(gòu)體指針,有 2 個(gè)各為 4 字節(jié)的 mask_t售葡。所以加起來就是 16 個(gè)字節(jié)看杭。也就是說前三個(gè)屬性總共的內(nèi)存偏移量為 8 + 8 + 16 = 32 個(gè)字節(jié),32 是 10 進(jìn)制的表示挟伙,在 16 進(jìn)制下就是 20楼雹。

3.2 探索 bits 屬性

我們剛才在控制臺(tái)打印輸出了 pClass 類對(duì)象的內(nèi)容,我們簡(jiǎn)單畫個(gè)圖如下所示:

image

那么尖阔,類的 bits 屬性的內(nèi)存地址順理成章的就是在 isa 的初始偏移量地址處進(jìn)行 16 進(jìn)制下的 20 遞增贮缅。也就是

0x1000021c8 + 0x20 = 0x1000021e8

我們嘗試打印這個(gè)地址,注意這里需要強(qiáng)轉(zhuǎn)一下:

image

這里報(bào)錯(cuò)了诺祸,問題其實(shí)是出在我們的 target 沒有關(guān)聯(lián)上 libobjc.A.dylib 這個(gè)動(dòng)態(tài)庫携悯,我們關(guān)聯(lián)上重新運(yùn)行項(xiàng)目

image

我們重復(fù)一遍上面的流程:

image

這一次成功了。在 objc_class 源碼中有:

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

我們不妨打印一下里面的內(nèi)容:

image

返回了一個(gè) class_rw_t 指針對(duì)象筷笨。我們?cè)?objc4-756 源碼中搜索 class_rw_t:

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;
    
    ...省略代碼...    
}

顯然的憔鬼,class_rw_t 也是一個(gè)結(jié)構(gòu)體類型,其內(nèi)部有 methods胃夏、properties轴或、protocols 等我們十分熟悉的內(nèi)容。我們先猜想一下仰禀,我們的屬性應(yīng)該存放在 class_rw_tproperties 里面照雁。為了驗(yàn)證我們的猜想,我們接著進(jìn)行 LLDB 打印:

image

我們?cè)俳又蛴?properties:

image

properties 居然是空的答恶,難道是 bug?其實(shí)不然饺蚊,這里我們還漏掉了一個(gè)非常重要的屬性 ro。我們來到它的定義:

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

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    ...隱藏代碼...    
}

ro 的類型是 class_ro_t 結(jié)構(gòu)體悬嗓,它包含了 baseMethodList污呼、baseProtocolsivars包竹、baseProperties 等屬性燕酷。我們剛才在 class_rw_t 中沒有找到我們聲明在 Person 類中的實(shí)例變量 hobby 和屬性 nickName,那么希望就在 class_ro_t 身上了周瞎,我們打印看看它的內(nèi)容:

image

根據(jù)名稱我們猜測(cè)屬性應(yīng)該在 baseProperties 里面苗缩,我們打印看看:

image

Bingo! 我們的屬性 nickName 被找到了,那么我們的實(shí)例變量 hobby 呢声诸?我們從 $8 的 count 為 1 可以得知肯定不在 baseProperites 里面。根據(jù)名稱我們猜測(cè)應(yīng)該是在 ivars 里面双絮。

image

哈哈浴麻,hobby 實(shí)例變量也被我們找到了得问,不過這里的 count 為什么是 2 呢?我們打印第二個(gè)元素看看:

image

結(jié)果為 _nickName软免。這一結(jié)果證實(shí)了編譯器會(huì)幫助我們給屬性 nickName 生成一個(gè)帶下劃線前綴的實(shí)例變量 _nickName宫纬。

至此,我們可以得出以下結(jié)論:

class_ro_t 是在編譯時(shí)就已經(jīng)確定了的膏萧,存儲(chǔ)的是類的成員變量漓骚、屬性、方法和協(xié)議等內(nèi)容榛泛。
class_rw_t 是可以在運(yùn)行時(shí)來拓展類的一些屬性蝌蹂、方法和協(xié)議等內(nèi)容。

四曹锨、類的方法存在哪孤个?

研究完了類的屬性是怎么存儲(chǔ)的,我們?cè)賮砜纯搭惖姆椒ā?/p>

我們先給我們的 Person 類增加一個(gè) sayHello 的實(shí)例方法和一個(gè) sayHappy 的類方法沛简。

// Person.h
- (void)sayHello;
+ (void)sayHappy;

// Person.m
- (void)sayHello
{
    NSLog(@"%s", __func__);
}

+ (void)sayHappy
{
    NSLog(@"%s", __func__);
}

按照上面的思路齐鲤,我們直接讀取 class_ro_t 中的 baseMethodList 的內(nèi)容:

image

sayHello 被打印出來了,說明 baseMethodList 就是存儲(chǔ)實(shí)例方法的地方椒楣。我們接著打印剩下的內(nèi)容:

image

可以看到 baseMethodList 中除了我們的實(shí)例方法 sayHello 外给郊,還有屬性 nickNamegettersetter 方法以及一個(gè) C++ 析構(gòu)方法。但是我們的類方法 sayHappy 并沒有被打印出來捧灰。

五淆九、類的類方法存在哪?

我們上面已經(jīng)得到了屬性毛俏,實(shí)例方法的是怎么樣存儲(chǔ)炭庙,還留下了一個(gè)疑問點(diǎn),就是類方法是怎么存儲(chǔ)的煌寇,接下來我們用 Runtime 的 API 來實(shí)際測(cè)試一下煤搜。

// main.m
void testInstanceMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
    Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));

    Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
    
    NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
    NSLog(@"%s",__func__);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Person *p = [[Person alloc] init];
        Class pClass = object_getClass(p);
        
        testInstanceMethod_classToMetaclass(pClass);
        NSLog(@"%p", p);
    }
    return 0;
}

運(yùn)行后打印結(jié)果如下:

image

首先 testInstanceMethod_classToMetaclass 方法測(cè)試的是分別從類和元類去獲取實(shí)例方法、類方法的結(jié)果唧席。由打印結(jié)果我們可以知道:

  • 對(duì)于類對(duì)象來說,sayHello 是實(shí)例方法嘲驾,存儲(chǔ)于類對(duì)象的內(nèi)存中淌哟,不存在于元類對(duì)象中。而 sayHappy 是類方法辽故,存儲(chǔ)于元類對(duì)象的內(nèi)存中徒仓,不存在于類對(duì)象中。
  • 對(duì)于元類對(duì)象來說誊垢,sayHello 是類對(duì)象的實(shí)例方法掉弛,跟元類沒關(guān)系症见;sayHappy 是元類對(duì)象的實(shí)例方法,所以存在元類中殃饿。

我們?cè)俳又鴾y(cè)試:

// main.m
void testClassMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getClassMethod(pClass, @selector(sayHello));
    Method method2 = class_getClassMethod(metaClass, @selector(sayHello));

    Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
    
    NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
    NSLog(@"%s",__func__);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Person *p = [[Person alloc] init];
        Class pClass = object_getClass(p);
        
        testClassMethod_classToMetaclass(pClass);
        NSLog(@"%p", p);
    }
    return 0;
}

運(yùn)行后打印結(jié)果如下:

image

從結(jié)果我們可以看出谋作,對(duì)于類對(duì)象來說,通過 class_getClassMethod 獲取 sayHappy 是有值的乎芳,而獲取 sayHello 是沒有值的遵蚜;對(duì)于元類對(duì)象來說,通過 class_getClassMethod 獲取 sayHappy 也是有值的奈惑,而獲取 sayHello 是沒有值的吭净。這里第一點(diǎn)很好理解,但是第二點(diǎn)會(huì)有點(diǎn)讓人糊涂肴甸,不是說類方法在元類中是體現(xiàn)為對(duì)象方法的嗎寂殉?怎么通過 class_getClassMethod 從元類中也能拿到 sayHappy,我們進(jìn)入到 class_getClassMethod 方法內(nèi)部可以解開這個(gè)疑惑:

Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    return class_getInstanceMethod(cls->getMeta(), sel);
}

Class getMeta() {
    if (isMetaClass()) return (Class)this;
    else return this->ISA();
}    

可以很清楚的看到原在,class_getClassMethod 方法底層其實(shí)調(diào)用的是 class_getInstanceMethod友扰,而 cls->getMeta() 方法底層的判斷邏輯是如果已經(jīng)是元類就返回,如果不是就返回類的 isa晤斩。這也就解釋了上面的 sayHappy 為什么會(huì)出現(xiàn)在最后的打印中了焕檬。

除了上面的 LLDB 打印,我們還可以通過 isa 的方式來驗(yàn)證類方法存放在元類中澳泵。

  • 通過 isa 在類對(duì)象中找到元類
  • 打印元類的 baseMethodsList

具體的過程筆者不再贅述实愚。

六、類和元類的創(chuàng)建時(shí)機(jī)

我們?cè)谔剿黝惡驮惖臅r(shí)候兔辅,對(duì)于其創(chuàng)建時(shí)機(jī)還不是很清楚腊敲,這里我們先拋出結(jié)論:

  • 類和元類是在編譯期創(chuàng)建的,即在進(jìn)行 alloc 操作之前维苔,類和元類就已經(jīng)被編譯器創(chuàng)建出來了碰辅。

那么如何來證明呢,我們有兩種方式可以來證明:

  • LLDB 打印類和元類的指針
image
  • 編譯項(xiàng)目后介时,使用 MachoView 打開程序二進(jìn)制可執(zhí)行文件查看:
image

六没宾、總結(jié)

  • 類和元類創(chuàng)建于編譯時(shí),可以通過 LLDB 來打印類和元類的指針沸柔,或者 MachOView 查看二進(jìn)制可執(zhí)行文件
  • 萬物皆對(duì)象:類的本質(zhì)就是對(duì)象
  • 類在 class_ro_t 結(jié)構(gòu)中存儲(chǔ)了編譯時(shí)確定的屬性循衰、成員變量、方法和協(xié)議等內(nèi)容褐澎。
  • 實(shí)例方法存放在類中
  • 類方法存放在元類中

我們完成了對(duì) iOS 中類的底層探索会钝,下一章我們將對(duì)類的緩存進(jìn)行深一步探索,敬請(qǐng)期待~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末工三,一起剝皮案震驚了整個(gè)濱河市迁酸,隨后出現(xiàn)的幾起案子先鱼,更是在濱河造成了極大的恐慌,老刑警劉巖奸鬓,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焙畔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡全蝶,警方通過查閱死者的電腦和手機(jī)闹蒜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抑淫,“玉大人绷落,你說我怎么就攤上這事∈嘉” “怎么了砌烁?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)催式。 經(jīng)常有香客問我函喉,道長(zhǎng),這世上最難降的妖魔是什么荣月? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任管呵,我火速辦了婚禮,結(jié)果婚禮上哺窄,老公的妹妹穿的比我還像新娘捐下。我一直安慰自己,他們只是感情好萌业,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布坷襟。 她就那樣靜靜地躺著,像睡著了一般生年。 火紅的嫁衣襯著肌膚如雪婴程。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天抱婉,我揣著相機(jī)與錄音档叔,去河邊找鬼。 笑死蒸绩,一個(gè)胖子當(dāng)著我的面吹牛蹲蒲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播侵贵,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼缘薛!你這毒婦竟也來了窍育?” 一聲冷哼從身側(cè)響起卡睦,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎漱抓,沒想到半個(gè)月后表锻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乞娄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年瞬逊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仪或。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡确镊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出范删,到底是詐尸還是另有隱情蕾域,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布到旦,位于F島的核電站旨巷,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏添忘。R本人自食惡果不足惜采呐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望搁骑。 院中可真熱鬧斧吐,春花似錦、人聲如沸靶病。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽娄周。三九已至涕侈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間煤辨,已是汗流浹背裳涛。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留众辨,地道東北人端三。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像鹃彻,于是被迫代替她去往敵國和親郊闯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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