iOS-底層-NSObject本質(zhì)

問題一:一個NSObject對象占用多少內(nèi)存宪哩?

一. 分析NSObject

1. 通過源碼分析

我們平時編寫的Objective-C代碼谷徙,底層實現(xiàn)其實都是C\C++代碼

底層

所以O(shè)bjective-C的面向?qū)ο蠖际腔贑\C++的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)的

思考:Objective-C的對象讶请、類主要是基于C\C++的什么數(shù)據(jù)結(jié)構(gòu)實現(xiàn)的蘸吓?
答案: 結(jié)構(gòu)體

如何將Objective-C代碼轉(zhuǎn)換為C\C++代碼?

首先創(chuàng)建一個命令行項目,只寫下面一句代碼:

NSObject *obj = [[NSObject alloc] init];

cd到main.m文件對應(yīng)的文件夾笛园,執(zhí)行以下指令:

xcrun  -sdk  iphoneos  clang  -arch  arm64  -rewrite-objc  OC源文件  -o  輸出的CPP文件

解釋:

  1. (使用xcode) (指定sdk跑在iOS平臺上) (用clang編譯器) (指定架構(gòu)arm64) (重寫objc文件) (OC源文件名稱) (輸出) (輸出文件名稱)
  2. 如果需要鏈接其他框架涂邀,使用-framework參數(shù)瘟仿。比如-framework UIKit
  3. 如果將C++文件添加到項目中,需要將C++文件移除編譯比勉,否則運(yùn)行報錯
  4. 在使用clang轉(zhuǎn)換OC為C++代碼時劳较,可能會遇到以下問題:
    cannot create __weak reference in file using manual reference
    解決方案:支持ARC、指定運(yùn)行時系統(tǒng)版本浩聋,比如:
    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m

在終端執(zhí)行xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp指令之后我們就把main.m文件轉(zhuǎn)換成C++文件了,打開 main-arm64.cpp文件,搜索int main(int,可以發(fā)現(xiàn)main函數(shù)被重寫成如下代碼

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

       NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));

    }
    return 0;
}

再搜索IMPL,發(fā)現(xiàn)如下結(jié)構(gòu)體,這就是底層通過C++定義的NSObject的結(jié)構(gòu)體

// NSObject Implementation NSObject底層實現(xiàn)
struct NSObject_IMPL {
    Class isa; // 指針在64位系統(tǒng)占8個字節(jié) 在32位系統(tǒng)占4字節(jié)
};

對比OC對于NSObject的定義,發(fā)現(xiàn)兩者其實是一樣的

// NSObject定義
@interface NSObject {
    Class isa;
}
@end

驗證了Objective-C的對象观蜗、類主要是基于C\C++的結(jié)構(gòu)體實現(xiàn)的

點(diǎn)進(jìn)入Class,發(fā)現(xiàn)isa其實就是一個指向結(jié)構(gòu)體的指針

// 指針
typedef struct objc_class *Class;

既然是指針, 指針在64位系統(tǒng)占8個字節(jié),在32位系統(tǒng)占4字節(jié)

現(xiàn)在我們就明白了,下面一句代碼在內(nèi)存中做了什么事了

NSObject *obj = [[NSObject alloc] init];
  1. 首先alloc之后,系統(tǒng)會給這個結(jié)構(gòu)體分配內(nèi)存,由于結(jié)構(gòu)體里面只有一個isa指針,所以isa的內(nèi)存地址就是結(jié)構(gòu)體的內(nèi)存地址,假設(shè)isa指針內(nèi)存地址為: 0x100400110,那么整個結(jié)構(gòu)體的內(nèi)存地址也是這個,整個NSObject對象的內(nèi)存地址也是這個

  2. 然后再用一個obj指針指向這個內(nèi)存地址(這個obj指針里面存放的就是這個對象的地址值)

內(nèi)存關(guān)系圖:

內(nèi)存關(guān)系圖

回到文章剛開始的問題,一個NSObject對象占用多少內(nèi)存?
可能你會說是8個,其實是16個字節(jié)

先了解兩個函數(shù):
size_t class_getInstanceSize(Class _Nullable cls)
獲取實例對象的成員變量所占用內(nèi)存大小(內(nèi)存對齊后的) -> 其實就是實例對象至少占用的內(nèi)存大小

size_t malloc_size(const void *ptr)
獲取指針?biāo)赶騼?nèi)存的大小 -> 其實就是實例對象實際占用的內(nèi)存大小

分別導(dǎo)入兩個函數(shù)對應(yīng)的頭文件

#import <objc/runtime.h>
#import <malloc/malloc.h>

打印:

 NSObject *obj = [[NSObject alloc] init];   // 16個字節(jié)

 // 獲得NSObject實例對象的成員變量所占用的大小 打印 8
 NSLog(@"%zd", class_getInstanceSize([NSObject class]));
        
 // 獲得obj指針?biāo)赶騼?nèi)存的大小 打印 16
 NSLog(@"%zd", malloc_size((__bridge const void *)obj));

第一個打印8,第二個打印16

回到問題一,我們不難發(fā)現(xiàn)問題的答案應(yīng)該是16

總結(jié):一個NSObject對象占用16字節(jié)的內(nèi)存

系統(tǒng)分配了16個字節(jié)給NSObject對象(通過malloc_size函數(shù)獲得)
但NSObject對象內(nèi)部只使用了8個字節(jié)的空間(64bit環(huán)境下衣洁,可以通過class_getInstanceSize函數(shù)獲得)

為什么通過class_getInstanceSize獲取的是8呢?

其實objc底層好多源碼是開源的,我們在https://opensource.apple.com/tarballs/搜索objc,點(diǎn)擊objc4文件夾進(jìn)去,下載一個最新的(數(shù)字最大的)

objc

解壓之后我們就能查看runtime源碼了,打開項目搜索class_getInstanceSize

//獲得的是內(nèi)存對齊后的大小  aligned對齊
size_t class_getInstanceSize(Class cls)
{
    if (!cls) return 0;
    return cls->alignedInstanceSize();
}

進(jìn)去alignedInstanceSize,可以看出返回的是ivar成員變量的內(nèi)存大小,所以打印的才是8

// Class's ivar size rounded up to a pointer-size boundary.
    uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }

為什么我們需要8字節(jié),系統(tǒng)給我們分配16字節(jié)呢?

現(xiàn)在我們看看alloc內(nèi)部是怎么實現(xiàn)的,其實alloc內(nèi)部調(diào)用的是allocWithZone,搜索_objc_rootAllocWithZone函數(shù)

①進(jìn)入class_createInstance -> _class_createInstanceFromZone

②在_class_createInstanceFromZone里面我們發(fā)現(xiàn)有一個函數(shù) obj = (id)calloc(1, size),其實這個calloc就是實際分配內(nèi)存的函數(shù),它傳入一個參數(shù)size

③進(jìn)入獲取size的函數(shù) size_t size = cls->instanceSize(extraBytes)

size_t instanceSize(size_t extraBytes) {
    //size_t size = class_getInstanceSize(Class) + extraBytes;)
    size_t size = alignedInstanceSize() + extraBytes;
    // CF requires all objects be at least 16 bytes.
    if (size < 16) size = 16;
    return size;
}

可以看出,如果size<16,size=16
現(xiàn)在我們就明白了墓捻,為什么OC對象至少占用16個字節(jié)了,因為系統(tǒng)的硬性規(guī)定坊夫。

觀察上面的代碼,發(fā)現(xiàn)上面的代碼也調(diào)用了class_getInstanceSize 的內(nèi)部方法alignedInstanceSize
所以,size_t size = alignedInstanceSize() + extraBytes其實就相當(dāng)于size_t size = class_getInstanceSize(Class) + extraBytes

2. 通過Xcode的viewMemory查看對象內(nèi)存結(jié)構(gòu)

下面我們換個方式砖第,使用Xcode的viewMemory查看obj對象內(nèi)存結(jié)構(gòu)

  1. 打斷點(diǎn),點(diǎn)擊obj,獲取到打印的內(nèi)存
點(diǎn)擊obj.png
  1. 進(jìn)入viewMemory
viewMemory.png
  1. 輸入地址
地址.png
  1. 查看內(nèi)存
內(nèi)存.png

圖中是使用16進(jìn)制的,一個16進(jìn)制位代表4個二進(jìn)制位,兩個16進(jìn)制位代表8個二進(jìn)制位 (一個字節(jié)占用8個二進(jìn)制位大小),所以兩個16進(jìn)制位代表一個字節(jié)
不明白的可參考:為什么一個字節(jié)占8個二進(jìn)制

可以發(fā)現(xiàn)前8個字節(jié)有值,后8個字節(jié)為0,可以猜想系統(tǒng)分配內(nèi)存的時候先清零,先分配了16個字節(jié),但是我只用8個,所以把前8個給用了,后8個字節(jié)還空著

3. 使用lldb指令查看內(nèi)存

除了使用viewMemory查看內(nèi)存,還可以使用lldb指令查看內(nèi)存:

常用指令:

print/p :打印
printobject/po :打印對象

讀取內(nèi)存:
memory read/打印數(shù)量+格式+字節(jié)數(shù) 內(nèi)存地址
x/打印數(shù)量+格式+字節(jié)數(shù) 內(nèi)存地址 (x是memory read簡寫)
例如: x/3xw 0x10010
(打印幾串)(用什么進(jìn)制)(每一串多少字節(jié))

修改內(nèi)存中的值:
memory write 內(nèi)存地址 數(shù)值
memory write 0x0000010 10

格式:
x是16進(jìn)制,f是浮點(diǎn)环凿,d是10進(jìn)制

字節(jié)數(shù):
b:byte 1字節(jié)梧兼,h:half word 2字節(jié)
w:word 4字節(jié),g:giant word 8字節(jié)

例如我們讀取上面的obj地址:

(lldb) p obj
(NSObject *) $0 = 0x0000000100766be0
(lldb) po obj
<NSObject: 0x100766be0>

(lldb) memory read 0x100766be0
0x100766be0: 41 91 e9 97 ff ff 1d 00 00 00 00 00 00 00 00 00  A...............
0x100766bf0: c0 6c 76 00 01 00 00 00 00 6f 76 00 01 00 00 00  .lv......ov.....

(lldb) memory read/3xg 0x100766be0
0x100766be0: 0x001dffff97e99141 0x0000000000000000
0x100766bf0: 0x0000000100766cc0

(lldb) x 0x100766be0
0x100766be0: 41 91 e9 97 ff ff 1d 00 00 00 00 00 00 00 00 00  A...............
0x100766bf0: c0 6c 76 00 01 00 00 00 00 6f 76 00 01 00 00 00  .lv......ov.....

(lldb) x/3xg 0x100766be0
0x100766be0: 0x001dffff97e99141 0x0000000000000000
0x100766bf0: 0x0000000100766cc0

(lldb) x/4xw 0x100766be0
0x100766be0: 0x97e99141 0x001dffff 0x00000000 0x00000000

(lldb) x/4dg 0x100766be0
0x100766be0: 8444247555019073
0x100766be8: 0
0x100766bf0: 4302728384
0x100766bf8: 4302728960
(lldb)

就按照x/(打印幾串)(用什么進(jìn)制)(每一串多少字節(jié))格式來就可以了

可以發(fā)現(xiàn)打印結(jié)果和使用viewMemory是一樣的,但是x 0x100766be0和x/3xg 0x100766be0打印的內(nèi)存信息展示方式卻是相反的,這是因為iOS都是小端模式,是從右往左讀取的,所以,內(nèi)存中分布是:41 91 e9 97 ff ff 1d 讀取出來就是:0x001dffff97e99141

修改內(nèi)存:
從上面可以看出,第一個字節(jié)的內(nèi)存地址是0x100766be0,我們想修改第十個字節(jié)的數(shù)據(jù)為9,指令為memory write 0x100766be9 9

(lldb) x 0x100766be0
0x100766be0: 41 91 e9 97 ff ff 1d 00 00 00 00 00 00 00 00 00  A...............
0x100766bf0: c0 6c 76 00 01 00 00 00 00 6f 76 00 01 00 00 00  .lv......ov.....

(lldb) memory write 0x100766be9 9

(lldb) x 0x100766be0
0x100766be0: 41 91 e9 97 ff ff 1d 00 00 09 00 00 00 00 00 00  A...............
0x100766bf0: c0 6c 76 00 01 00 00 00 00 6f 76 00 01 00 00 00  .lv......ov.....
(lldb) 

可以發(fā)現(xiàn)第十個字節(jié)被改成09了

二. 分析Student

上面我們分析是最簡單的NSObject類智听,現(xiàn)在我們定義一個有兩個成員變量的Student類羽杰,如下捷泞,查看它的內(nèi)存情況拓劝。

首先我們要知道指針占用8字節(jié),int類型數(shù)據(jù)占用4字節(jié)今豆。

@interface Student : NSObject
{
    @public
    int _no;
    int _age;
}
@end

使用上面相同的方法重寫為C++文件,在文件中搜索Student_IMPL,結(jié)果如下:

struct Student_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _no;
    int _age;
};

上面我們已經(jīng)知道NSObject_IMPL結(jié)構(gòu)體就是NSObject的底層定義,里面只有一個isa指針

//NSObject的實現(xiàn) 這個結(jié)構(gòu)體只占用8個字節(jié),只不過硬性分配給他16個字節(jié)
struct NSObject_IMPL {
    Class isa;
};

所以Student類內(nèi)部的底層的結(jié)構(gòu)體實現(xiàn)其實就是:

struct Student_IMPL {
    //struct NSObject_IMPL NSObject_IVARS;
    Class isa;
    int _no;
    int _age;
};

賦值:

Student *stu = [[Student alloc] init];
stu->_no = 4;
stu->_age = 5;

現(xiàn)在Student對象內(nèi)部的內(nèi)存分布情況我們就很明白了

內(nèi)存.png

由于這個結(jié)構(gòu)體中的第一個值的內(nèi)存地址是0x100400110 所以整個結(jié)構(gòu)體的內(nèi)存地址也是0x100400110
,所以Student對象的內(nèi)存地址也是0x100400110,所以stu指針里面存放的地址也是0x100400110

再次驗證對象本質(zhì)就是結(jié)構(gòu)體:
下面用結(jié)構(gòu)體指針指向stu,再通過結(jié)構(gòu)體直接訪問成員變量,訪問成功,說明對象本質(zhì)就是結(jié)構(gòu)體

struct Student_IMPL *stuImpl = (__bridge struct Student_IMPL *)stu;
NSLog(@"no is %d, age is %d", stuImpl->_no, stuImpl->_age);
//no is 4, age is 5

同樣我們使用viewMemory驗證,發(fā)現(xiàn)4和5的確在內(nèi)存里面
內(nèi)存.png

同樣使用上面兩個函數(shù)打印

NSLog(@"%zd", class_getInstanceSize([Student class]));
//16
NSLog(@"%zd", malloc_size((__bridge const void *)stu));
//16  

可以發(fā)現(xiàn)Student對象里的成員變量一共占用16個字節(jié)莉测,stu指針指的內(nèi)存也是占用16個字節(jié)颜骤,其中前面8個字節(jié)放isa,后面4字節(jié)個放_no悔雹,最后4個字節(jié)放_age复哆。

它們在內(nèi)存中是連續(xù)的,內(nèi)存圖如下:

分布.png

三. 分析Person和Student

我們定義更復(fù)雜的類,如下

// Person
@interface Person : NSObject
{
    @public
    int _age;
}
@end

//Student
@interface Student : Person
{
    int _no;
}
@end

Student繼承于Person

Person *person = [[Person alloc] init];
person->_age = 20;
Student *stu = [[Student alloc] init]; 
stu->_no = 10;

class_getInstanceSize 獲取的是對齊后的內(nèi)存大小
NSLog(@"person - %zd", class_getInstanceSize([Person class])); //16
NSLog(@"person - %zd", malloc_size((__bridge const void *)person)); //16

NSLog(@"stu - %zd", class_getInstanceSize([Student class]));  //16
NSLog(@"stu - %zd", malloc_size((__bridge const void *)stu));  //16

可以發(fā)現(xiàn)打印都是16,分析:
對于person,底部代碼為:

struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS; // 8
    int _age; // 4
}; // 一共16字節(jié)

就算沒OC源碼里面的至少為16字節(jié)的規(guī)定,由于結(jié)構(gòu)體的內(nèi)存對齊:結(jié)構(gòu)體的大小必須是最大成員大小的倍數(shù) 從這個角度看也是16字節(jié)腌零。

對于Student,底部源碼是:

struct Student_IMPL {
    struct Person_IMPL Person_IVARS; // 16
    int _no; // 4
}; // 一共16字節(jié)  

雖然Person_IMPL占用16字節(jié),但是他有4字節(jié)空出來的,所以_no正好放在那里

他們內(nèi)存結(jié)構(gòu)圖如下:

內(nèi)存.png

當(dāng)然你也可以使用viewMemory和lldb查看內(nèi)存情況,這里就省略了

如果給Person添加一個屬性,內(nèi)存中是什么樣呢?

@property (nonatomic, assign) int height;

查看C++文件,看出底層是這樣的

struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS; //8字節(jié)
    int _no; //4字節(jié)
    int _height //4字節(jié)
}; //一共16字節(jié)

可以看出實例對象的內(nèi)存中多了一個_height梯找,沒有setter和getter方法。

setter和getter方法為什么不和成員變量放一塊呢?
因為方法一份就夠了,多個對象都可以調(diào)用,沒必要放實例對象的內(nèi)存中,其實方法放到類對象和方法列表里面)

四. 解答最后一個疑問益涧,引入iOS的內(nèi)存對齊

創(chuàng)建如下類

@interface MJPerson : NSObject
{
    int _age;
    int _height;
    int _no;
}
@end

通過上面的學(xué)習(xí),我么你很容易知道它底層是這樣的

struct NSObject_IMPL
{
    Class isa;
};

struct MJPerson_IMPL
{
    struct NSObject_IMPL NSObject_IVARS; // 8
    int _age; // 4
    int _height; // 4
    int _no; // 4
}; // 計算結(jié)構(gòu)體大小锈锤,按照結(jié)構(gòu)體內(nèi)存對齊,24

然后我們按照最少16字節(jié),結(jié)構(gòu)體的內(nèi)存對齊要是8的倍數(shù)來分析,這個結(jié)構(gòu)體至少需要占用24字節(jié)

接下來我們打印:

MJPerson *p = [[MJPerson alloc] init];
NSLog(@"%zd", sizeof(struct MJPerson_IMPL)); // 24
NSLog(@"%zd %zd",
      class_getInstanceSize([MJPerson class]), // 24
      malloc_size((__bridge const void *)(p))); // 32

可以發(fā)現(xiàn),結(jié)構(gòu)體實際需要24字節(jié),但是系統(tǒng)卻給Person對象32字節(jié),為什么呢?

首先,我們還是查看源碼,按照剛開始的查看alloc底層方法調(diào)用的順序,我們會發(fā)現(xiàn)如下兩個熟悉的方法

size_t size = cls->instanceSize(extraBytes);
obj = (id)calloc(1, size);

按照剛開始我們的分析instanceSize其實就相當(dāng)于class_getInstanceSize,所以它返回的就是24,但是在calloc函數(shù)里面把24傳進(jìn)入怎么就變成32了呢?

你可能會說再查看calloc底層不就好了,calloc的底層在liamalloc庫里面,也是在https://opensource.apple.com/tarballs/里面下載,但是分析太麻煩了闲询,我就直接說結(jié)論了久免。

結(jié)論:

因為ios系統(tǒng)也有內(nèi)存對齊的概念,內(nèi)存必須是16的倍數(shù)扭弧,就算你只需要24字節(jié)阎姥,傳給我24,我也會傳給你32字節(jié)鸽捻。
這也解釋了呼巴,為什么NSObject里面只有一個isa指針(占8字節(jié)),但是還是給他16字節(jié)的原因了御蒲。

小補(bǔ)充: sizeof

  1. sizeof和class_getInstanceSize都是返回至少需要多少內(nèi)存,而malloc_size返回的是實際需要的
  2. 但是他們也有不同點(diǎn):sizeof傳進(jìn)來一個類型進(jìn)來,我告訴你類型有多大,比如int,sizeof是個運(yùn)算符,不是函數(shù),所以上面我們使用sizeof打印的時候需要傳入結(jié)構(gòu)體NSLog(@"%zd", sizeof(struct MJPerson_IMPL)); // 24

就算你這樣寫

MJPerson *p = [[MJPerson alloc] init];
NSLog(@"%zd", sizeof(p)); //8

就算你把p傳進(jìn)去,它打印的也是8,因為你把一個指針(占用8字節(jié))傳進(jìn)去了,因為sizeof是個運(yùn)算符,所以在編譯的時候就已經(jīng)確定是8了,就相當(dāng)于

MJPerson *p = [[MJPerson alloc] init];
NSLog(@"%zd", 8); //8
  1. class_getInstanceSize傳一個類進(jìn)來,我告訴你最終創(chuàng)建的實例大小,是個函數(shù)

總結(jié):

  1. Objective-C的對象衣赶、類主要是基于C\C++的結(jié)構(gòu)體實現(xiàn)的. NSObject對象底層是個結(jié)構(gòu)體,結(jié)構(gòu)體內(nèi)部只有一個isa指針。
  2. 一個指針占8個字節(jié)厚满,所以結(jié)構(gòu)體實際需要8字節(jié)府瞄,但是一個NSObject對象卻占用16個字節(jié)(因為iOS的內(nèi)存對齊或者說系統(tǒng)規(guī)定至少占用16字節(jié)內(nèi)存)。
  3. 分析對象內(nèi)存的時候不要忘記結(jié)構(gòu)體內(nèi)存對齊(結(jié)構(gòu)體的大小必須是最大成員大小的倍數(shù)碘箍,一般是8)和iOS內(nèi)存對齊(對象內(nèi)存大小必須是16的倍數(shù))遵馆。
  4. 兩個獲取內(nèi)存大小的函數(shù)
    ① size_t class_getInstanceSize(Class _Nullable cls)
    獲取實例對象的成員變量所占用內(nèi)存大小(內(nèi)存對齊后的)-> 其實就是實例對象至少占用的內(nèi)存大小丰榴。
    sizeof同上团搞,返回的是傳入類型至少占用的內(nèi)存大小,sizeof是個運(yùn)算符多艇。
    ② size_t malloc_size(const void *ptr)
    獲取指針?biāo)赶騼?nèi)存的大小 -> 其實就是實例對象實際占用的內(nèi)存大小逻恐。

Demo地址:NSObject本質(zhì)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市峻黍,隨后出現(xiàn)的幾起案子复隆,更是在濱河造成了極大的恐慌,老刑警劉巖姆涩,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挽拂,死亡現(xiàn)場離奇詭異,居然都是意外死亡骨饿,警方通過查閱死者的電腦和手機(jī)亏栈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門台腥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人绒北,你說我怎么就攤上這事黎侈。” “怎么了闷游?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵峻汉,是天一觀的道長。 經(jīng)常有香客問我脐往,道長休吠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任业簿,我火速辦了婚禮瘤礁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘梅尤。我一直安慰自己蔚携,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布克饶。 她就那樣靜靜地躺著酝蜒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪矾湃。 梳的紋絲不亂的頭發(fā)上亡脑,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機(jī)與錄音邀跃,去河邊找鬼霉咨。 笑死,一個胖子當(dāng)著我的面吹牛拍屑,可吹牛的內(nèi)容都是我干的途戒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼僵驰,長吁一口氣:“原來是場噩夢啊……” “哼喷斋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蒜茴,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤星爪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后粉私,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體顽腾,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年诺核,在試婚紗的時候發(fā)現(xiàn)自己被綠了抄肖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片久信。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖漓摩,靈堂內(nèi)的尸體忽然破棺而出裙士,到底是詐尸還是另有隱情,我是刑警寧澤幌甘,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布潮售,位于F島的核電站痊项,受9級特大地震影響锅风,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鞍泉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一皱埠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧咖驮,春花似錦边器、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至睦刃,卻和暖如春砚嘴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背涩拙。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工际长, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人兴泥。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓工育,卻偏偏與公主長得像,于是被迫代替她去往敵國和親搓彻。 傳聞我的和親對象是個殘疾皇子如绸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354