Swift進(jìn)階(二)--- 類,對(duì)象

Swift編譯簡(jiǎn)介

我們先來(lái)開(kāi)一段簡(jiǎn)單的代碼:

class SuperMan  {
    var age:Int = 18
    var name:String = "Aaron"
}
let t = SuperMan()

我們創(chuàng)建一個(gè)SuperMan類肉津,并通過(guò)默認(rèn)的初始化器强胰,創(chuàng)建一個(gè)實(shí)例對(duì)象并賦值給了t
那么問(wèn)題來(lái)了:這個(gè)默認(rèn)的初始化器到底做了一個(gè)什么樣的操作妹沙?
這里我們引入SIL(Swift intermediate language)偶洋,在閱讀SIL的代碼之前,我們先來(lái)了解一下什么是SIL

首先不管是OC還是Swift距糖,后端都是通過(guò)LLVM進(jìn)行編譯的玄窝,如下圖所示:

image.png

可以看到:
OC通過(guò)clang編譯器,編譯成IR肾筐,然后再生成可執(zhí)行文件.o(這里也就是我們的機(jī)器碼)
Swift則是通過(guò)Swift 編譯器編譯成IR哆料,然后再生成可執(zhí)行文件
我們?cè)賮?lái)看一下,一個(gè)swift文件的編譯過(guò)程都經(jīng)歷了哪些步驟:
image.png

swift在編譯過(guò)程中使用的前端編譯器是swiftc,和我們之前在OC中使用的Clang是有所區(qū)別的吗铐。我們可以通過(guò)swiftc -h命令揉阎,來(lái)查看swiftc都能做什么事情

image.png

SIL

SIL參考文檔

  • 首先進(jìn)入工程所在的文件夾伤疙,然后執(zhí)行swiftc -emit-sil main.swift | xcrun swift-demangle >> ./main.sil && code main.sil 指令兵睛。對(duì)該指令有疑問(wèn)的同學(xué),可以點(diǎn)擊這里奋渔,鏈接文章的最后,有對(duì)該指令的詳細(xì)講解壮啊。下方代碼是用VSCode打開(kāi)的main.sil文件嫉鲸,
// t
sil_global hidden @main.t : main.SuperMan : $SuperMan

// main
// '@main': 標(biāo)識(shí)當(dāng)前main.swift的入口函數(shù)。SIL中的標(biāo)識(shí)符名稱以‘@’作為前綴
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
//'%0歹啼、%1' 在SIL中叫做寄存器玄渗,可以理解為開(kāi)發(fā)中的常量,一旦賦值就不可修改狸眼,如果想繼續(xù)使用藤树,就需要不算的累加數(shù)字(注意:這里的寄存器指的是‘虛擬寄存器’,與匯編指令‘register read %x’讀取的寄存器不同拓萌,匯編指令讀取的是真實(shí)的寄存器)
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
//‘a(chǎn)lloc_global’ 創(chuàng)建一個(gè)‘全局變量’ --> ‘t
  alloc_global @main.t : main.SuperMan           // id: %2
//‘global_addr’ 獲取全局變量的地址岁钓,并賦值給%3
  %3 = global_addr @main.t : main.SuperMan : $*SuperMan // user: %7
//‘metatype’ 獲取‘Superman’ 的 ‘MetaData’ 賦值給 %4
  %4 = metatype $@thick SuperMan.Type             // user: %6
  // function_ref SuperMan.__allocating_init()
//將 ‘__allocating_init()’ 函數(shù)的地址賦值給 %5
  %5 = function_ref @main.SuperMan.__allocating_init() -> main.SuperMan : $@convention(method) (@thick SuperMan.Type) -> @owned SuperMan // user: %6
//’apply‘ 調(diào)用 ’__allocating_init()‘ 并將返回值 賦值給 %6  
  %6 = apply %5(%4) : $@convention(method) (@thick SuperMan.Type) -> @owned SuperMan // user: %7
//’store‘ 將 %6 的值 存儲(chǔ)到 %3,也就是前面提到的全局變量的地址
  store %6 to %3 : $*SuperMan                     // id: %7
//創(chuàng)建一個(gè)整數(shù)文字值微王,類型為Builtin.Int32屡限,該類型必須為內(nèi)置整數(shù)類型。文字值是使用Swift的整數(shù)文字語(yǔ)法指定的炕倘,值為0钧大,使用者%8
  %8 = integer_literal $Builtin.Int32, 0          // user: %9
//構(gòu)建結(jié)構(gòu)體
  %9 = struct $Int32 (%8 : $Builtin.Int32)        // user: %10
  return %9 : $Int32                              // id: %10
} // end sil function 'main'
  • 下面我們結(jié)合匯編進(jìn)行一下斷點(diǎn)調(diào)試。查看一下SuperMan的創(chuàng)建流程罩旋,如下圖打斷點(diǎn)
    image.png

    Dubge中選擇Always Show Disassembly
    image0.png

    接下來(lái)我們會(huì)在匯編代碼中發(fā)現(xiàn)一個(gè)SuperMan.__allocating_init()
    image1.png

    我們使用si指令跟進(jìn)去會(huì)發(fā)現(xiàn)該方法的內(nèi)部會(huì)調(diào)用SuperMan.init()函數(shù)拓型,并且會(huì)先調(diào)用swift_allocObject函數(shù)。
    image2.png

源碼調(diào)試

根據(jù)上面的分析瘸恼,接下來(lái)我們通過(guò)swift_allocObject來(lái)探索swift中對(duì)象的創(chuàng)建過(guò)程,通過(guò)搜索swift_allocObject我們找到了該函數(shù)的實(shí)現(xiàn)

image3.png

  • 接下來(lái)我們來(lái)進(jìn)行斷點(diǎn)調(diào)試


    image4.png
  • 可以看到左側(cè)local有詳細(xì)的信息
  • 其中requiredSize是分配的實(shí)際內(nèi)存大小册养,為40东帅。其中metadata:8, refCounts:8, age:8, name:16。
  • requiredAligmentMask是swift中的字節(jié)對(duì)其方式球拦,這個(gè)和OC中的是一樣的靠闭,必須是8的倍數(shù),不足的會(huì)自動(dòng)補(bǔ)齊坎炼,目的是以空間換時(shí)間愧膀,來(lái)提高內(nèi)存的操作效率。
    注意:這里的requiredSizerequiredAligmentMask都是上層函數(shù)傳進(jìn)來(lái)的

swift_allocObject 源碼分析

swift_allocObject的源碼如下谣光,主要有一下幾部分組成

  • 通過(guò)swift_slowAlloc分配內(nèi)存檩淋,并進(jìn)行內(nèi)存字節(jié)對(duì)齊。
  • 通過(guò)new + HeapObject + metadata 初始化一個(gè)實(shí)例對(duì)象
  • 函數(shù)的返回值是HeapObject 類型萄金,所以當(dāng)前對(duì)象的內(nèi)存結(jié)構(gòu)就是HeapObject的內(nèi)存結(jié)構(gòu)
    image5.png
  • 進(jìn)入swift_slowAlloc函數(shù)蟀悦,其內(nèi)部主要是通過(guò)malloc中分配size大小的內(nèi)存空間媚朦,并返回內(nèi)存地址,主要是用于存儲(chǔ)實(shí)例變量
    image6.png
  • 進(jìn)入HeapObject初始化方法日戈,可以看到兩個(gè)參數(shù):metadata询张、refCounts
    image7.png
  • 其中metadata類型是HeapMetadata,是一個(gè)指針類型浙炼,占8個(gè)字節(jié)
  • refCounts(引用計(jì)數(shù)份氧,類型是InlineRefCounts,而InlineRefCounts是一個(gè)RefCounts的別名弯屈,占8個(gè)字節(jié))
typedef RefCounts<InlineRefCountBits> InlineRefCounts;
image8.png

總結(jié)

  • 對(duì)于實(shí)例對(duì)象t來(lái)說(shuō)蜗帜,其本質(zhì)是一個(gè)HeapObject結(jié)構(gòu)體,默認(rèn)16字節(jié)內(nèi)存大小(metadata8字節(jié) + refCounts8字節(jié))季俩,與OC對(duì)比如下:
    • OC中實(shí)例對(duì)象的本質(zhì)是結(jié)構(gòu)體钮糖,是以objc_object為模板繼承的,其中有一個(gè)isa指針酌住,占8字節(jié)店归。
    • Swift中實(shí)例對(duì)象,默認(rèn)的比OC中多一個(gè)refCounts引用計(jì)數(shù)酪我,默認(rèn)屬性占16字節(jié)消痛。
  • Swift中對(duì)象的分配流程是:__allocating_init--->swift_allocObject--->_swift_allocObject_--->swift_slowAlloc-->malloc
  • init在其中的職責(zé)就是初始化變量都哭,這點(diǎn)與OC中的是一致的秩伞。

看到這里,有的同學(xué)可能會(huì)有一些疑問(wèn)?:為什么 agename分別是 8字節(jié)16字節(jié)?
接下來(lái)我們?cè)敿?xì)講解一下:
對(duì)于Int欺矫、String類型纱新,在Swift中,兩個(gè)都是結(jié)構(gòu)體類型穆趴。我可以通過(guò)內(nèi)存打印來(lái)驗(yàn)證一下

//********* Int底層定義 *********
@frozen public struct Int : FixedWidthInteger, SignedInteger {...}

//********* String底層定義 *********
@frozen public struct String {...}

//********* 驗(yàn)證 *********
print("Int類型所占內(nèi)存大辛嘲:\(MemoryLayout<Int>.stride)")
print("String類型所占內(nèi)存大小:\(MemoryLayout<String>.stride)")

print("t.age所占內(nèi)存大形疵谩:\(MemoryLayout.size(ofValue: t.age))")
print("t.name所占內(nèi)存大胁痉稀:\(MemoryLayout.size(ofValue: t.name))")
//********* 打印結(jié)果 *********
Int類型所占內(nèi)存大小:8
String類型所占內(nèi)存大新缢:16
t.age所占內(nèi)存大凶迕省:8
t.name所占內(nèi)存大小:16

通過(guò)上面的打印結(jié)果化戳,可以清楚的看到IntString所占內(nèi)存大小单料。

但是到這里,可能同學(xué)問(wèn)了,那如果String的值非常大的話看尼,內(nèi)存里面分配的16個(gè)自己又該怎么存儲(chǔ)呢递鹉?
swift的內(nèi)存分配的問(wèn)題,之后會(huì)單獨(dú)寫一篇文章來(lái)詳細(xì)講一下藏斩,這里就不做詳細(xì)的贅述躏结,這里記住Int占8個(gè)字節(jié),String占16個(gè)字節(jié)就可以了狰域。其它的類型媳拴,可自行打印出來(lái)看一下。

下面我們來(lái)看另一個(gè)問(wèn)題兆览。我們上面提到了metadata屈溉,那它到底是什么呢?我們繼續(xù)往下分析

Swift中 類結(jié)構(gòu)的探索

在OC中類是從objc_class模板繼承過(guò)來(lái)的抬探。
在Swift中子巾,類的結(jié)構(gòu)在底層是HeapObject,包含metadatarefCounts小压。
metadata的類型是HeapMetadata

HeapMetadata類型分析

  • 通過(guò)Swift源碼线梗,跟進(jìn)去我們會(huì)發(fā)現(xiàn)
using HeapMetadata = TargetHeapMetadata<InProcess>
  • 我們接著跟進(jìn)TargetHeapMetadata
template <typename Runtime>
struct TargetHeapMetadata : TargetMetadata<Runtime> {
  using HeaderType = TargetHeapMetadataHeader<Runtime>;

  TargetHeapMetadata() = default;
//初始化方法
  constexpr TargetHeapMetadata(MetadataKind kind)
    : TargetMetadata<Runtime>(kind) {}
#if SWIFT_OBJC_INTEROP
  constexpr TargetHeapMetadata(TargetAnyClassMetadata<Runtime> *isa)
    : TargetMetadata<Runtime>(isa) {}
#endif
};
  • 進(jìn)入TargetHeapMetadata定義后,我們發(fā)現(xiàn)其本質(zhì)是一個(gè)模板類型怠益。這個(gè)結(jié)構(gòu)體中沒(méi)有屬性仪搔,只有初始化方法,傳入一個(gè)MetadataKind類型的參數(shù)蜻牢,這里kind就是傳入的Inprocess

  • 接下來(lái)烤咧,我們繼續(xù)跟進(jìn)到TargetMetadata中,有一個(gè)kind的屬性抢呆,kind的類型就是前面?zhèn)魅氲?code>Inprocess煮嫌。然后我們?cè)俑M(jìn)Inprocess,最總得到結(jié)果是:對(duì)于kind抱虐,其類型的本質(zhì)是unsigned long

//******** TargetMetaData 定義 ********
struct TargetMetadata {
  using StoredPointer = typename Runtime::StoredPointer
  ...
  private:
  /// The kind. Only valid for non-class metadata; getKind() must be used to get
  /// the kind value.
  StoredPointer Kind
}
//******** Inprocess 定義 ********
struct InProcess {
  static constexpr size_t PointerSize = sizeof(uintptr_t);
  using StoredPointer = uintptr_t;
  ...
}
//******** uintptr_t 定義 ********
typedef unsigned long           uintptr_t;
  • TargetHeapMetadata立膛、TargetMetadata定義中,我可以看出kind的類型是MetadataKind(此處是強(qiáng)制轉(zhuǎn)化梯码,與上文并不沖突)
  • 跟進(jìn)MetadataKind,可以看到一個(gè)#include "MetadataKind.def"好啰,再次跟進(jìn)轩娶,會(huì)看到所有類型的元數(shù)據(jù)
    image9.png

    總結(jié)之后得到如下表格:
name Value
Class 0x0
Struct 0x200
Enum 0x201
Optional 0x202
ForeignClass 0x203
Opaque 0x300
Tuple 0x301
Function 0x302
Existential 0x303
Metatype 0x304
ObjCClassWrapper 0x305
ExistentialMetatype 0x306
HeapLocalVariable 0x400
HeapGenericLocalVariable 0x500
ErrorObject 0x501
LastEnumerated 0x7FF
  • 回到TargetMetadata結(jié)構(gòu)體定義中,找到方法getClassObject, 在該方法中去匹配kind框往,返回值TargetClassMetadata類型
  • 如果是Class鳄抒,則直接對(duì)this(當(dāng)前指針,即metadata) 強(qiáng)轉(zhuǎn)為ClassMetadata
  /// Get the class object for this type if it has one, or return null if the
  /// type is not a class (or not a class with a class object).
  const TargetClassMetadata<Runtime> *getClassObject() const

//*************  方法的實(shí)現(xiàn) **************
  template<> inline const ClassMetadata *
  Metadata::getClassObject() const {
    //匹配kind
    switch (getKind()) {
    //如果kind是class
    case MetadataKind::Class: {
      // Native Swift class metadata is also the class object.
      // 將當(dāng)前指針強(qiáng)轉(zhuǎn)為ClassMetadata類型
      return static_cast<const ClassMetadata *>(this);
    }
    case MetadataKind::ObjCClassWrapper: {
      // Objective-C class objects are referenced by their Swift metadata wrapper.
      auto wrapper = static_cast<const ObjCClassWrapperMetadata *>(this);
      return wrapper->Class;
    }
    // Other kinds of types don't have class objects.
    default:
      return nullptr;
    }
  
//************** ClassMetadata ****************
using ClassMetadata = TargetClassMetadata<InProcess>;

所以,TargetMetadataTargetClassMetadata 本質(zhì)上是一樣的许溅,因?yàn)樵趦?nèi)存結(jié)構(gòu)中瓤鼻,可以直接進(jìn)行指針的轉(zhuǎn)換,所以我們可以認(rèn)為:結(jié)構(gòu)體其實(shí)就是TargetClassMetadata

  • 進(jìn)入TargetClassMetadata定義贤重,發(fā)現(xiàn)其繼承自TargetAnyClassMetadata
template <typename Runtime>
struct TargetClassMetadata : public TargetAnyClassMetadata<Runtime> {
...
/// Swift-specific class flags. 
///Swift 特有的標(biāo)識(shí)
  ClassFlags Flags;

  /// The address point of instances of this type. 
  ///此類型的實(shí)例對(duì)象的地址
  uint32_t InstanceAddressPoint;

  /// The required size of instances of this type.
  /// 'InstanceAddressPoint' bytes go before the address point;
  /// 'InstanceSize - InstanceAddressPoint' bytes go after it.
 /// 實(shí)例對(duì)象內(nèi)存大小
  uint32_t InstanceSize;

  /// The alignment mask of the address point of instances of this type.
  /// 實(shí)例對(duì)象內(nèi)存對(duì)齊
  uint16_t InstanceAlignMask;

  /// Reserved for runtime use.
  /// 運(yùn)行時(shí)保留字段
  uint16_t Reserved;

  /// The total size of the class object, including prefix and suffix
  /// extents.
  /// 類的內(nèi)存大小
  uint32_t ClassSize;

  /// The offset of the address point within the class object.
  /// 類的內(nèi)存首地址
  uint32_t ClassAddressPoint;
...
}
  • 進(jìn)入TargetAnyClassMetadata茬祷,又會(huì)發(fā)現(xiàn),其繼承自TargetHeapMetadata
template <typename Runtime>
struct TargetAnyClassMetadata : public TargetHeapMetadata<Runtime> {
  using StoredPointer = typename Runtime::StoredPointer;
  using StoredSize = typename Runtime::StoredSize
...
}

總結(jié)

綜上所述并蝗,當(dāng)metadatakind為Class時(shí)祭犯,有如下繼承關(guān)系:

image10.png

  • 當(dāng)前類返回的實(shí)際類型是TargetClassMetadata;從上面繼承鏈可以看出 TargetMetadata只有一個(gè)屬性kind滚停,TargetAnyClassMetaData中有4個(gè)屬性:kind沃粗、superClasscacheData键畴、data最盅。
  • 當(dāng)前Class在內(nèi)存中存放的屬性,是由TargetClassMetadata + TargetAnyClassMetadata + TargetMetadata 的屬性加起來(lái)構(gòu)成的起惕,因此得出metadata的數(shù)據(jù)結(jié)構(gòu)體如下:
struct swift_class_t:NSObject {
    void *kind;//相當(dāng)于OC中的isa涡贱,kind的實(shí)際類型是unsigned long
    void *superClass;
    void *cacheData;
    void *data;
    uint32_t flages; //4字節(jié)
    uint32_t instanceAddressOffset; //4字節(jié)
    uint32_t instanceSize; //4字節(jié)
    uint16_t instanceAlignMask; //2字節(jié)
    uint16_t reserved; //2字節(jié)

    uint32_t classSize; //4字節(jié)
    uint32_t classAddressOffset; //4字節(jié)
    void *description
    ...
}

與OC對(duì)比

  • 實(shí)例對(duì)象 & 類
    • OC中的實(shí)例對(duì)象本質(zhì)是結(jié)構(gòu)體,是通過(guò)底層的objc_object模板創(chuàng)建的疤祭,類是繼承自objc_class
    • Swift中的實(shí)例對(duì)象本質(zhì)也是結(jié)構(gòu)體盼产,類型是HeapObject,比OC多了一個(gè)refCounts
  • 方法列表
    • OC中的方法儲(chǔ)存在objc_class結(jié)構(gòu)體class_rw_tmethodList
    • Swift中的方法儲(chǔ)存在metadata元數(shù)據(jù)中
  • 引用計(jì)數(shù)
    • OC中的ARC維護(hù)的是散列表
    • Swift中的ARC是對(duì)象內(nèi)部有一個(gè)refCounts屬性
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末勺馆,一起剝皮案震驚了整個(gè)濱河市戏售,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌草穆,老刑警劉巖灌灾,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異悲柱,居然都是意外死亡锋喜,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門豌鸡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嘿般,“玉大人,你說(shuō)我怎么就攤上這事涯冠÷” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵蛇更,是天一觀的道長(zhǎng)瞻赶。 經(jīng)常有香客問(wèn)我赛糟,道長(zhǎng),這世上最難降的妖魔是什么砸逊? 我笑而不...
    開(kāi)封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任璧南,我火速辦了婚禮,結(jié)果婚禮上师逸,老公的妹妹穿的比我還像新娘司倚。我一直安慰自己,他們只是感情好字旭,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布对湃。 她就那樣靜靜地躺著,像睡著了一般遗淳。 火紅的嫁衣襯著肌膚如雪拍柒。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天屈暗,我揣著相機(jī)與錄音拆讯,去河邊找鬼。 笑死养叛,一個(gè)胖子當(dāng)著我的面吹牛种呐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播弃甥,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼爽室,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了淆攻?” 一聲冷哼從身側(cè)響起阔墩,我...
    開(kāi)封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瓶珊,沒(méi)想到半個(gè)月后啸箫,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伞芹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年忘苛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片唱较。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扎唾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出南缓,到底是詐尸還是另有隱情胸遇,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布西乖,位于F島的核電站狐榔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏获雕。R本人自食惡果不足惜薄腻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望届案。 院中可真熱鬧庵楷,春花似錦、人聲如沸楣颠。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)童漩。三九已至弄贿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間矫膨,已是汗流浹背差凹。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留侧馅,地道東北人危尿。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像馁痴,于是被迫代替她去往敵國(guó)和親谊娇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

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