NSObject 對象占用內(nèi)存蠢正、isa/superclass指向、類信息存放

2018年08月26日

  • 添加參考使用源碼版本說明

2018年08月23日

  • 添加實例對象映琳、類對象机隙、元類對象信息存放指示圖,更加直觀解析了OC的類信息存放位置

2018年08月12日

  • 修改了獲取類對象時萨西,傳入?yún)?shù)錯誤問題
  • 添加objc_getClass 和 object_getClass方法源碼區(qū)別

2018年08月05日

  • 修改第1點的回答方式有鹿,更詳細(xì)

注:分析步驟參考 MJ底層原理班 內(nèi)容,本著自己學(xué)習(xí)原則記錄

本文使用的源碼為objc4-723

1 一個NSObject對象占用多少內(nèi)存谎脯?

  1. OC底層實現(xiàn)是C/C++葱跋,OC 對象的底層表現(xiàn)為 C/C++的結(jié)構(gòu)體
  2. 結(jié)構(gòu)體的大小,實際上是指它內(nèi)部所有成員變量占用內(nèi)存的大小
    (存在內(nèi)存對齊原則源梭,指的是結(jié)構(gòu)體的內(nèi)存大小必須是最大成員變量內(nèi)存的大小的倍數(shù)關(guān)系)
  3. 在64bit 下娱俺,NSObject類的結(jié)構(gòu)體對象只包含一個 Class 類型指針的 isa 成員變量
  4. 按照第3點的理解,NSObject 對象占用的內(nèi)存應(yīng)該就只有8個字節(jié)的空間
    (64bit 下废麻,可以通過class_getInstanceSize函數(shù)獲得荠卷,其內(nèi)部會進行內(nèi)存對齊操作)
  5. 但實際情況是:系統(tǒng)分配了16個字節(jié)給 NSObject 對象
    (通過 malloc_size 函數(shù)獲得)

以下是上述5點的解析:

1.1 OC 代碼通過兩種方法獲得的大小

  • 使用 Xcode 創(chuàng)建 macOS 類的 command line 項目,代碼如下:
#import <Foundation/Foundation.h>

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

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *obj = [[NSObject alloc] init];
        
        // 獲得 NSObject 類實例對象的成員變量所占用的大小
        NSLog(@"%zd", class_getInstanceSize([NSObject class]));
        
        // 獲得 obj 指針指向內(nèi)存的大小
        NSLog(@"%zd", malloc_size((__bridge const void *)obj));
        
    }
    return 0;
}


>>>> 打印結(jié)果
 8
16

1.2 class_getInstanceSizemalloc_size說明

  • API 說明
extern size_t malloc_size(const void *ptr);
    /* Returns size of given ptr */
/** 
 * Returns the size of instances of a class.
 * 
 * @param cls A class object.
 * 
 * @return The size in bytes of instances of the class \e cls, or \c 0 if \e cls is \c Nil.
 */
OBJC_EXPORT size_t
class_getInstanceSize(Class _Nullable cls) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
  • 兩個函數(shù)使用場景
創(chuàng)建一個實例對象烛愧,至少需要多少內(nèi)存油宜?
#import <objc/runtime.h>
class_getInstanceSize([NSObject class]); 

等價于 sizeof()獲得的值。
sizeof 獲取類型大小怜姿,它是一個運算符并非函數(shù)慎冤,在編譯時即計算到給定類型的大小,即如 sizeof(int) 在編譯后會直接替換為 4沧卢。 
由于在編譯時計算蚁堤,因此sizeof不能用來返回動態(tài)分配的內(nèi)存空間的大小,而class_getInstanceSize則屬于動態(tài)獲取
創(chuàng)建一個實例對象但狭,實際上分配了多少內(nèi)存披诗?
#import <malloc/malloc.h>
malloc_size((__bridge const void *)obj);

1.3 通過源碼解析class_getInstanceSize方法返回8個字節(jié)原因

size_t class_getInstanceSize(Class cls)
{
    if (!cls) return 0;
    return cls->alignedInstanceSize();
}
    // Class's ivar size rounded up to a pointer-size boundary.
    uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }
  • 函數(shù)alignedInstanceSize的描述是 Class's ivar size rounded up to a pointer-size
  • 這個方法是獲取類 ivar(成員變量) 的大小,這就解析為什么方法class_getInstanceSize返回的是8個字節(jié)了

因為 NSObject 對象中只有一個 isa 指針成員變量藤巢,而且 isa 的類型是一個指針搞莺。在64bit 設(shè)備下指針大小為8個字節(jié)

1.4 將 OC 轉(zhuǎn)成 C/C++代碼, 解析NSObject本質(zhì)

  • OC 中 NSObject 的定義掂咒,只有一個 isa 指針
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
  • 通過命令行將 OC 的 mian.m 文件轉(zhuǎn)化為 C++ 文件

方式一:簡單轉(zhuǎn)換

clang -rewrite-objc main.m -o main.cpp // 這種方式?jīng)]有指定架構(gòu)才沧,如 arm64 架構(gòu)生成 main.cpp

方式二:使用xcode工具 xcrun,指定架構(gòu)模式

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp 

在生成的 main-arm64.cpp 文件中搜索NSObjcet绍刮,可以找到NSObjcet_IMPL(IMPL代表 implementation 實現(xiàn))

  • C++ 的結(jié)構(gòu)體
struct NSObject_IMPL {
    Class isa;
};
// Class其實就是一個指針温圆,類型如下
// typedef struct objc_class *Class;

1.5 為什么有8個字節(jié)又有16個字節(jié)的問題呢?

  • 通過在源碼中追蹤allocWithZone函數(shù)獲得解答
    我們知道孩革,創(chuàng)建對象時岁歉,NSObject *obj = [[NSObject alloc] init];會調(diào)用alloc類方法,而其底層就是調(diào)用allocWithZone
  1. 底層都是調(diào)用 callAlloc

    搜索 allocWithZone 獲取對應(yīng)信息

  2. class_createInstance方法創(chuàng)建 obj

    class_createInstance

  3. 找到分配內(nèi)存函數(shù)instanceSize

  4. 原因: corefoundation 要求所有 objects 最少16 bytes


    最少16 bytes

1.6 簡單繼承的對象內(nèi)存占用分析

  • 一個Person對象膝蜈、一個Student對象占用多少內(nèi)存空間锅移?
#import <Foundation/Foundation.h>

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


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

@implementation Person
@end

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

@implementation Student
@end



int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Person *person = [[Person alloc] init];
        NSLog(@"person - %zd", class_getInstanceSize([Person class]));
        NSLog(@"person - %zd", malloc_size((__bridge const void *)person));
        
        
        Student *stu = [[Student alloc] init];
        NSLog(@"stu - %zd", class_getInstanceSize([Student class]));
        NSLog(@"stu - %zd", malloc_size((__bridge const void *)stu));
        
    }
    return 0;
}

>>>>打印結(jié)果
person - 16
person - 16
stu - 16
stu - 16

1.6.1 轉(zhuǎn)成 C++ 后結(jié)構(gòu)體成員分析

  • 下述代碼分析可以通過上述對 NSObject 對象的分析步驟獲得


    摘自 MJ 底層原理班課程 PPT

1.6.2 內(nèi)存對齊

  1. 內(nèi)存對齊:結(jié)構(gòu)體的大小必須是最大成員大小的倍數(shù)
  2. Person 中的實際應(yīng)該分配應(yīng)該是12個字節(jié),因為 isa 為8字節(jié)饱搏,int 類型的 _age 為4字節(jié)非剃,為什么class_getInstanceSize返回的還是16字節(jié)呢?此時就要考慮內(nèi)存對齊了推沸。以最大成員大小备绽,即8字節(jié)的 isa 的倍數(shù)算,最少就是8的2倍鬓催,16字節(jié)了肺素。
struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS; // 8
    int _age; // 4
}; // 16 內(nèi)存對齊:結(jié)構(gòu)體的大小必須是最大成員大小的倍數(shù)

1.6.3 優(yōu)先利用空的連續(xù)的內(nèi)存

  1. 上述中 Person 實例實際使用的內(nèi)存是12字節(jié),但是內(nèi)存占用是16字節(jié)宇驾,那么多余的4個字節(jié)在 Student 實例創(chuàng)建時就需要被考慮使用了倍靡。
struct Student_IMPL {
    struct Person_IMPL Person_IVARS; // 16
    int _no; // 4
}; // 16,剛好 Person 分配的16字節(jié)中空余的4個字節(jié)可以放下 int 類型的 _no 成員變量

1.7 帶@property的對象內(nèi)存占用分析

// Person
@interface Person : NSObject
{
    int _age;
}
@property (nonatomic, assign) int height;
@end

@implementation Person
@end


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Person *person = [[Person alloc] init];
        NSLog(@"person - %zd", class_getInstanceSize([Person class]));
        NSLog(@"person - %zd", malloc_size((__bridge const void *)person));

    }
    return 0;
}

>>>>打印結(jié)果
person - 16
person - 16
  1. @property 是作用是自動生成一個帶下劃線的實例變量课舍,同時生成對應(yīng)的getter 和 setter 方法
  2. 那么轉(zhuǎn)換成 C++ 后代碼如下
struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS; // 8
    int _age; // 4
    int _height; // 4
};  // 16
  1. 也就如運行所得菌瘫,占用內(nèi)存為16個字節(jié)

1.8 為什么實例方法不在實例對象里呢?

  1. 實例方法是公用的布卡,一份足以應(yīng)付同一類型的多個實例對象。因為除了實例變量的值會變之外雇盖,方法的調(diào)用是不會變的忿等。也就是 person1、person2崔挖、person3 它們調(diào)用 Person 類的方法都是一樣的贸街。

1.9 附加課程介紹的內(nèi)存分析和修改內(nèi)存的操作

  • 分析 stu 實例內(nèi)存
    OC 代碼
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>

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

@implementation Student
@end


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Student *stu = [[Student alloc] init];
        stu->_no = 4;
        stu->_age = 5;
        
        NSLog(@"%zd", class_getInstanceSize([Student class]));
        NSLog(@"%zd", malloc_size((__bridge const void *)stu));
        
    }
    return 0;
}

C++ 代碼

struct NSObject_IMPL {
    Class isa; // 8
};


struct Student_IMPL {
    Class isa;
    int _no;
    int _age;
};

1.9.1 實時查看內(nèi)存數(shù)據(jù)

  1. 在 stu 生成后庵寞,打斷點。
  2. 在 Xcode 的控制器查看 stu 實時地址
  3. 在Xcode 工具欄 選擇 Debug -> Debug Workfllow -> View Memory (Shift + Command + M)然后在 address 中輸入對象的地址
    View Memory
輸入地址
  • 注意薛匪,上圖讀取內(nèi)存時捐川,存在大端小端讀取方向問題。

從上圖中逸尖,我們可以發(fā)現(xiàn)讀取數(shù)據(jù)從高位數(shù)據(jù)開始讀古沥,查看前16位字節(jié),每四個字節(jié)讀出的數(shù)據(jù)為 16進制 0x00 00 00 04(4字節(jié))娇跟、 0x00 00 00 05(4字節(jié))岩齿、 isa的地址為 0x 00 D1 08 10 00 00 11 19(8字節(jié))

1.9.2 LLDB 指令查看且修改內(nèi)存值

1.9.2.1 在生成 stu 實例后,打斷點苞俘,啟動 LLDB盹沈。

1.9.2.2 通過 p 指令獲得 stu 的地址

(lldb) p stu
(Student *) $0 = 0x000000010062cdd0

1.9.2.3 通過指令memory read讀取對應(yīng)的地址內(nèi)存

(lldb) memory read 0x000000010062cdd0
0x10062cdd0: c9 11 00 00 01 80 1d 00 04 00 00 00 05 00 00 00  ................
0x10062cde0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
(lldb) 

指令memory read 可以簡寫成 x

(lldb) x 0x000000010062cdd0
0x10062cdd0: c9 11 00 00 01 80 1d 00 04 00 00 00 05 00 00 00  ................
0x10062cde0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
(lldb) 

1.9.2.4 增加讀取條件

memory read/[數(shù)量][格式][字節(jié)數(shù)] 內(nèi)存地址
簡寫:
x/[數(shù)量][格式][字節(jié)數(shù)] 內(nèi)存地址

格式:x是16進制,f是浮點吃谣,d是10進制
字節(jié)大衅蚍狻:b:byte 1字節(jié),h:half word 2字節(jié)岗憋,w:word 4字節(jié)肃晚,g:giant word 8字節(jié)

示例:x/4xw
/后面表示如何讀取數(shù)據(jù)
w:表示4個4個字節(jié)讀取
x:表示以16進制的方式讀取數(shù)據(jù)
4:則表示讀取4次

(lldb) memory read/4xw 0x000000010062cdd0
0x10062cdd0: 0x000011c9 0x001d8001 0x00000004 0x00000005

簡寫

(lldb) x/4xw 0x000000010062cdd0
0x10062cdd0: 0x000011c9 0x001d8001 0x00000004 0x00000005
(lldb) 

1.9.2.5 修改內(nèi)存中的值

  • 如,修改_no 的值為8則:
// 對象地址 +8個字節(jié)就是 _no 的地址
(lldb) memory write 0x000000010062cdd8 8
  • 通過 log 日志查看執(zhí)行步驟
(lldb) p stu
(Student *) $0 = 0x0000000100600590
2018-07-08 11:08:51.663461+0800 Test1 [21943:3939579] no is 4, age is 5
(lldb) memory write 0x0000000100600598 8
2018-07-08 11:09:15.525190+0800 Test1[21943:3939579] -------------
2018-07-08 11:09:15.525299+0800 Test1[21943:3939579] no is 8, age is 5
Program ended with exit code: 0
  • _no 的值在第一個斷點執(zhí)行前澜驮,通過命令memory write 0x0000000100600598 8進行修改了陷揪。值從4 變成 8

1.10 OC對象內(nèi)存分配對齊規(guī)則為16的倍數(shù)(最大是256)

  • 下述對象中即使實際占大小為24,但由于內(nèi)存分配對齊原則杂穷,最終分配給對象內(nèi)存就是32
#import <Foundation/Foundation.h>
#import <malloc/malloc.h>
#import <objc/runtime.h>

// C++代碼中對象的結(jié)構(gòu)體表示
//struct NSObject_IMPL
//{
//    Class isa;
//};
//
//struct Person_IMPL
//{
//    struct NSObject_IMPL NSObject_IVARS; // 8
//    int _age; // 4
//    int _height; // 4
//    int _no; // 4
//}; // 24


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

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
        
        NSLog(@"%zd", sizeof(struct Person_IMPL)); // 24
        
        NSLog(@"%zd %zd",
              class_getInstanceSize([Person class]), // 24
              malloc_size((__bridge const void *)(p))); // 32
    }
    return 0;
}

PS:更復(fù)雜的內(nèi)存分配后續(xù)補充

2. 對象的isa指針指向哪里悍缠?

問題簡答:

  1. instance對象的isa指針指向class對象
  2. class對象的isa指針指向meta-class對象
  3. meta-class對象的isa指針指向基類的meta-class對象
  4. 基類自己的isa指針也指向自己

問題理解方向如下:

2.1 OC對象的分類

  • 主要可以分為3種
    instance對象(實例對象)
    class對象(類對象)
    meta-class對象(元類對象)

2.2 instance對象在內(nèi)存中存儲的信息,主要包括

  • isa指針
  • 其他成員變量(具體的值的信息等)
  • ...

2.3 class對象在內(nèi)存中存儲的信息耐量,主要包括

  • isa指針
  • superclass指針
  • 類的成員變量信息(ivar)(變量名稱之類)
  • 類的屬性信息(@property)
  • 類的協(xié)議信息(protocol)
  • 類的對象方法信息(instance method)
  • ...

2.4 meta-class對象和class對象的內(nèi)存結(jié)構(gòu)是一樣的飞蚓,但是用途不一樣,在內(nèi)存中存儲的信息廊蜒,主要包括

  • isa指針
  • superclass指針
  • 類的類方法信息(class method)
  • ...

2.5 instance趴拧、class、meta-class 存儲區(qū)別

  • 每一個類通過 alloc 創(chuàng)建的 instance都是獨立占用一塊內(nèi)存的
NSObject *obj1 = [[NSObject alloc] init];
NSObject *obj2 = [[NSObject alloc] init];

obj1 和 obj2 是NSObject的instance對象(實例對象)山叮,分別占用兩塊不同的內(nèi)存
  • 每個類在內(nèi)存中有且只有一個class對象
Class objClass1 = [obj1 class];
Class objClass2 = [obj2 class];
Class objClass3 = [NSObject class];
Class objClass4 = object_getClass(obj1); //Runtime API
Class objClass5 = object_getClass(obj2); //Runtime API

objClass1~5 都是NSObject 的 class 對象著榴,它們都是同一個對象
  • 每個類在內(nèi)存中有且只有一個meta-class對象
Class objMetaClass = object_getClass([NSObject class]); // Runtime API
//類對象作為參數(shù)獲取元類對象

objMetaClass是NSObject的meta-class對象(元類對象)
  • 注意:
    以下方式獲得的是 class 對象,不是 meta-class對象
    Class objClass = [[NSObject class] class];
    
    查看 Class 是否為meta-class
    #import <objc/runtime.h>
    BOOL result = class_isMetaClass([NSObject   class]);
    

2.6 isa 和 superclass 指向總結(jié)

isa 和 superclass 指向圖屁倔,也是實例方法和類方法查找路線圖
  • 上圖解讀
  1. instance的isa指向class

  2. class的isa指向meta-class

  3. meta-class的isa指向基類的meta-class

  4. class的superclass指向父類的class
    如果沒有父類脑又,superclass指針為nil

  5. meta-class的superclass指向父類的meta-class
    基類的meta-class的superclass指向基類的class

  6. instance調(diào)用對象方法的軌跡
    isa找到class,方法不存在,就通過superclass找父類

  7. class調(diào)用類方法的軌跡
    isa找meta-class问麸,方法不存在往衷,就通過superclass找父類

2.7 objc_getClass 和 object_getClass

1. objc_getClass

  1. 根據(jù)類名字符串返回一個類對象
  2. 與 -class、+clas 方法返回結(jié)果一樣严卖,都只返回類對象席舍,即使繼續(xù)多次調(diào)用都不會返回 meta-class對象
objc_getClass

2. object_getClass

  1. 如果傳入的是 instance對象 則返回 class對象
  2. 如果傳入的是 class對象 則返回 meta-class對象
  3. 如果傳入是 meta-class對象 則返回 NSObject(基類/rootObject)的 meta-class對象
object_getClass

3. objc_getClass、object_getClass 和 class 小結(jié)

3. OC的類信息存放在哪里哮笆?

  • instance對象含有信息為:
    1. 成員變量的具體值(ivar value)

  • class對象含有信息為:
    1.對象方法(instance method)
    2. 協(xié)議(protocol)
    3. 屬性(property)
    4. 成員變量信息(ivar type and name etc.info)

  • meta-class對象含有信息為:
    1. 類方法(class method)


    摘自 MJ 底層課課件

文/Jacob_LJ(簡書作者)
PS:如非特別說明来颤,所有文章均為原創(chuàng)作品,著作權(quán)歸作者所有疟呐,轉(zhuǎn)載需聯(lián)系作者獲得授權(quán)脚曾,并注明出處,所有打賞均歸本人所有启具!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末本讥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鲁冯,更是在濱河造成了極大的恐慌拷沸,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件薯演,死亡現(xiàn)場離奇詭異撞芍,居然都是意外死亡,警方通過查閱死者的電腦和手機跨扮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門序无,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人衡创,你說我怎么就攤上這事帝嗡。” “怎么了璃氢?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵哟玷,是天一觀的道長。 經(jīng)常有香客問我一也,道長巢寡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任椰苟,我火速辦了婚禮抑月,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘舆蝴。我一直安慰自己爪幻,他們只是感情好菱皆,可當(dāng)我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著挨稿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪京痢。 梳的紋絲不亂的頭發(fā)上奶甘,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天,我揣著相機與錄音祭椰,去河邊找鬼臭家。 笑死,一個胖子當(dāng)著我的面吹牛方淤,可吹牛的內(nèi)容都是我干的钉赁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼携茂,長吁一口氣:“原來是場噩夢啊……” “哼你踩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起讳苦,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤带膜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鸳谜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體膝藕,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年咐扭,在試婚紗的時候發(fā)現(xiàn)自己被綠了芭挽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡蝗肪,死狀恐怖袜爪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情穗慕,我是刑警寧澤饿敲,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站逛绵,受9級特大地震影響怀各,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜术浪,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一瓢对、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧胰苏,春花似錦硕蛹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽秧荆。三九已至,卻和暖如春埃仪,著一層夾襖步出監(jiān)牢的瞬間乙濒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工卵蛉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留颁股,地道東北人。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓傻丝,卻偏偏與公主長得像甘有,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子葡缰,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,086評論 2 355