Swift--結(jié)構(gòu)體和類

結(jié)構(gòu)體

  • 在Swift標(biāo)準(zhǔn)庫中掀潮,絕大多數(shù)的公開類型都是結(jié)構(gòu)體骄呼,而枚舉和類只占很小一部分
  • 比如Bool共苛、IntDouble蜓萄、String隅茎、ArrayDictionary等常見類型都是結(jié)構(gòu)體
1嫉沽、struct Data {
2辟犀、    var year:Int
3、    var month:Int
4绸硕、  var day:Int
5堂竟、}
6、var date = Data(year: 2019, month: 6, day: 23)
  • 所有的結(jié)構(gòu)體都有一個(gè)編譯器自動(dòng)生成的初始化器(initializer玻佩,初始化方法出嘹,構(gòu)造器,構(gòu)造方法)
  • 在第6行調(diào)用的咬崔,可以傳入所有成員值税稼,用以初始化所有成員(存儲(chǔ)屬性烦秩,Stored Property)
  • 編譯器會(huì)根據(jù)情況,可能會(huì)為結(jié)構(gòu)體生成多個(gè)初始化器郎仆,宗旨是:保證所有成員都有初始值



struct Point {
    var x:Int?
    var y:Int?
}
var p1 = Point(x: 10, y: 10)
var p2 = Point(y:10)
var p3 = Point(x:10)
var p4 = Point()
  • 上述代碼可以編譯通過只祠,因?yàn)榭蛇x項(xiàng)都有個(gè)默認(rèn)值nil
  • 一旦在定義結(jié)構(gòu)體時(shí)自定義了初始化器,編譯器就不會(huì)再幫它自動(dòng)生成其他初始化器


初始化器

  • 所有的結(jié)構(gòu)體都有一個(gè)編譯器自動(dòng)生成的初始化器(initializer扰肌,初始化方法)
  • 類需要自己生成有成員值的初始化器抛寝,Swift中創(chuàng)建類和結(jié)構(gòu)體的實(shí)例時(shí)必須為所有的存儲(chǔ)屬性設(shè)置一個(gè)合適的初始值。所以類必須要提供對(duì)應(yīng)的指定初始化器曙旭,同時(shí)我們也可以為當(dāng)前的類提供便捷初始化器(注意:便捷初始化器必須從相同的類里調(diào)用另一個(gè)初始化器)
class LGPerson {
    var age:Int
    var name:String
    init(_ age:Int,_ name:String) {
        self.age = age
        self.name = name
    }
    convenience init(_ age:Int){//便捷初始化器
        self.init(18,"kody")
        self.age = age
        self.name = ""
    }
}

注意點(diǎn)

1墩剖、指定初始化器必須保證在向上委托給父類初始化器之前,其所在類引入的所有屬性都要初始化完成
2夷狰、指定初始化器必須先向上委托父類初始化器,然后才能為繼承的屬性設(shè)置新值郊霎,如果不這樣做沼头,指定初始化器賦予的新值將被父類中的初始化器所覆蓋
3、便捷初始化器必須先委托同類中的其他初始化器书劝,然后再為任意屬性賦新值(包括同類里定義的屬性)进倍。如果沒這么做,便捷初始化器賦予的新值將被自己類中其它指定初始化器所覆蓋
4购对、初始化器在第一階段初始化完成之前猾昆,不能調(diào)用任何實(shí)例方法、不能讀取任何實(shí)例屬性的值骡苞,也不能引用self作為值
class LGPerson {
    var age:Int
    var name:String
    init(_ age:Int,_ name:String) {
        self.age = age
        self.name = name
    }
    convenience init(_ age:Int){//便捷初始化器
        self.init(18,"kody")
        self.age = age
        self.name = ""
    }
}
class LGTeacher:LGPerson{
  var subjectName:String
  init (_ subjectName: String){
    self.subjectName = subjectName
    super.init(18,"awe")
  }
}
  • 可失敗初始化器:當(dāng)前因?yàn)閰?shù)的不合法或者外部條件的不滿足垂蜗,存在初始化失敗的情況。這種Swift中可失敗初始化器寫return nil語句解幽,來表明可失敗初始化器在何種情況下會(huì)觸發(fā)初始化失敗贴见。寫法如下:
class LGPerson {
    var age:Int
    var name:String
    init?(age:Int,name:String) {
        if age<18 {return nil}
        self.age = age
        self.name = name
    }
}
  • 必要初始化器:在類的初始化器前添加required修飾符來表明所有該類的子類都必須實(shí)現(xiàn)該初始化器

初始化器的本質(zhì)

struct Point {
    var x:Int = 0
    var y:Int = 0
}
var p1 = Point()
struct Point {
    var x:Int
    var y:Int
    init(){
      self.x = 0
      self.y = 0
    }
}
var p1 = Point()

上面兩段代碼完全等效

結(jié)構(gòu)體內(nèi)存結(jié)構(gòu)

struct Point {
    var x:Int = 0
    var y:Int = 0
    var origin:Bool = false
    
}
var p1 = Point()
print(MemoryLayout<Point>.size)//17
print(MemoryLayout<Point>.stride)//24
print(MemoryLayout<Point>.alignment)//8

結(jié)構(gòu)體和類的主要相同點(diǎn)

  • 定義存儲(chǔ)值的屬性
  • 定義方法
  • 定義下標(biāo)以使用下標(biāo)語法提供對(duì)其值的訪問
  • 定義初始化值
  • 使用extension來拓展功能
  • 遵循協(xié)議來提供某種功能

結(jié)構(gòu)體和類的主要不同點(diǎn)

  • 類有繼承的特性,而結(jié)構(gòu)體沒有
  • 類型轉(zhuǎn)換使您能夠在運(yùn)行時(shí)檢查和解釋類實(shí)例的類型
  • 類有析構(gòu)函數(shù)用來釋放其分配的資源
  • 引用計(jì)數(shù)允許對(duì)一個(gè)類實(shí)例有多個(gè)引用

結(jié)構(gòu)體與類的本質(zhì)區(qū)別

  • 類是引用類型(指針類型)躲株,也就意味著一個(gè)類類型的變量并不直接存儲(chǔ)具體的實(shí)例對(duì)象片部,是對(duì)當(dāng)前存儲(chǔ)具體實(shí)例內(nèi)存地址的引用
  • 結(jié)構(gòu)體是值類型(枚舉也是值類型),相比較類類型的變量中存儲(chǔ)的是地址霜定,那么值類型存儲(chǔ)的就是具體的實(shí)例(或者說具體的值)

類舉例

  • 類的定義和結(jié)構(gòu)體類似档悠,但編譯器并沒有為類自動(dòng)生成可以傳入成員值的初始化器



  • 如果類的所有成員都在定義的時(shí)候指定了初始值,編譯器會(huì)為類生成無參的初始化器
  • 成員的初始化是在這個(gè)初始化器中完成的
class Point {
    var x:Int = 10
    var y:Int = 20
}
let p1 = Point()
class Point {
    var x:Int
    var y:Int
    init() {
        x = 10
        y = 20
    }
}
let p1 = Point()

上面兩段代碼完全等效

  • 上圖都是針對(duì)64bit環(huán)境
    lldb指令:
    po/p:po和p的區(qū)別在于使用po只會(huì)輸出對(duì)應(yīng)的值望浩,而p則會(huì)返回值的類型以及命令結(jié)果的引用名
    x/8g:讀取內(nèi)存中的值(8g:8字節(jié)格式輸出)
class LGTeacher {
    var age:Int
    var name:String
    init(age:Int,name:String) {
        self.age = age
        self.name = name
    }
}
var t = LGTeacher(age: 18, name: "Kody")
var t1 = t;

print("end")//打斷點(diǎn)

lldb

(lldb) po t
<LGTeacher: 0x10064e3d0>

(lldb) po t1
<LGTeacher: 0x10064e3d0>

(lldb) x/8g 0x10064e3d0
0x10064e3d0: 0x0000000100008180 0x0000000600000003
0x10064e3e0: 0x0000000000000012 0x0000000079646f4b
0x10064e3f0: 0xe400000000000000 0x0000000000000000
0x10064e400: 0x00000009a0080001 0x00007fff817025c8

(lldb) po withUnsafePointer(to: &t, {print($0)})  //獲取t的地址
0x0000000100008218
(lldb) po withUnsafePointer(to: &t1, {print($0)})  //獲取t1的地址
0x0000000100008220
  • 靜態(tài)的存儲(chǔ)屬性(static)辖所,也就是類型存儲(chǔ)屬性,他在程序運(yùn)行過程中曾雕,只初始化一次奴烙,因?yàn)楸举|(zhì)就是全局變量,全局變量在運(yùn)動(dòng)過程中只初始化一次,而且static修飾的屬性默認(rèn)是lazy

結(jié)構(gòu)體舉例

struct LGTeacher {
    var age:Int
    var name:String
    init(age:Int,name:String) {
        self.age = age
        self.name = name
    }
}
var t = LGTeacher(age: 18, name: "Kody")
var t1 = t;

print("end")

lldb

(lldb) po t
? LGTeacher
  - age : 18
  - name : "Kody"

(lldb) po t1
? LGTeacher
  - age : 18
  - name : "Kody"

值類型

  • 值類型賦值給var切诀、let或者給函數(shù)傳參揩环,是直接將所有內(nèi)容拷貝一份
  • 類似于對(duì)文件進(jìn)行copy、paste操作幅虑,產(chǎn)生了全新的文件副本丰滑。屬于深拷貝(deep copy)
  • 值類型存儲(chǔ)在棧上


struct Point {
    var x:Int
    var y:Int
}

func test()  {
    var p1 = Point(x: 10, y: 20)
    var p2 = p1
    p2.x = 11
    p2.y = 22
//p1.x是10,p1.y是20
}

值類型的賦值操作

var s1 = "jack"
var s2 = s1
s2.append("_Rose")
print(s1)//jack
print(s2)//jack_Rose

var a1 = [1,2,3]
var a2 = a1
a2.append(4)
a1[0] = 2
print(a1)//[2,2,3]
print(a2)//[1,2,3,4]

var d1 = ["max":10,"min":2]
var d2 = d1
d1["other"] = 7
d2["max"] = 12
print(d1)//["other":7,"max":10,"min":2]
print(d2)//["max":12,"min":2]
  • 在Swift標(biāo)準(zhǔn)庫中倒庵,為了提升性能褒墨,String、Array擎宝、Dictiionary郁妈、Set采取了Copy On Write技術(shù),以String為例
var s1 = "Jack"
var s2 = s1
如果后續(xù)的代碼绍申,沒有對(duì)s1和s2做修改噩咪,則s1和s2指向同一地址,即淺拷貝极阅,如果后續(xù)有對(duì)s1或者s2做數(shù)據(jù)修改胃碾,則s1和s2會(huì)開出不同的地址做存儲(chǔ),則深拷貝
  • 比如僅當(dāng)有“寫”操作時(shí)筋搏,才會(huì)真正執(zhí)行拷貝操作
  • 對(duì)于標(biāo)準(zhǔn)庫值類型的賦值操作仆百,Swift能確保最佳性能,所以沒必要為了保證最佳性能來避免賦值
  • 建議:不需要修改的奔脐,盡量定義成let
struct Point {
    var x:Int
    var y:Int
}
var p1 = Point(x: 10, y: 20)
p1 = Point(x: 11, y: 22)
存儲(chǔ)地址是一樣的俄周,只是替換值

引用類型

  • 引用賦值給varlet或者給函數(shù)傳參髓迎,是將內(nèi)存地址拷貝一份
  • 類似于制作一個(gè)文件的替身(快捷方式栈源、鏈接),指向的是同一個(gè)文件竖般,屬于淺拷貝
  • 引用類型存儲(chǔ)在堆上
class Size {
    var width:Int
    var height:Int
    init(width:Int,height:Int) {
        self.width = width
        self.height = height
    }
}

func test() {
    var s1 = Size(width: 10, height: 20)
    var s2 = s1
}
內(nèi)存區(qū)域
棧區(qū)(Stack):局部變量和函數(shù)運(yùn)行過程中的上下文
堆區(qū)(Heap):存儲(chǔ)所有對(duì)象
全局區(qū)(Global))(全局區(qū)也可以細(xì)分為全局區(qū)甚垦、常量區(qū)、text指令區(qū)):存儲(chǔ)全局變量涣雕、常量艰亮、代碼區(qū)
Segment&Section:Mach-o文件有多個(gè)段(segment),每個(gè)段有不同的功能挣郭,然后每個(gè)段又分為很多小的section
TEXT.text :機(jī)器碼
TEXT.cstring:硬編碼的字符串
TEXT.const:初始化過的常量
DATA.data:初始化過的可變的(靜態(tài)/全局)數(shù)據(jù)
DATA.const:沒有初始化過的常量
DATA.bss:沒有初始化的(靜態(tài)/全局)變量
DATA.common:沒有初始化過的符號(hào)聲明

引用類型的賦值操作

class Size {
    var width:Int
    var height:Int
    init(width:Int,height:Int) {
        self.width = width
        self.height = height
    }
}
var s1 = Size(width: 10, height: 20)
s1 = Size(width: 11, height: 22)

值類型迄埃、引用類型的let

struct Point {
    var x:Int
    var y:Int
}

class Size {
    var width:Int
    var height:Int
    init(width:Int,height:Int) {
        self.width = width
        self.height = height
    }
}

let p = Point(x: 10, y: 20)
p = Point(x: 11, y: 22)//error:Cannot assign to value: 'p' is a 'let' constant
p.x = 33//error:Cannot assign to property: 'p' is a 'let' constant
p.y = 44//error:Cannot assign to property: 'p' is a 'let' constant

let s = Size(width: 10, height: 20)
s = Size(width: 11, height: 22)//error:Cannot assign to value: 's' is a 'let' constant
s.width = 33
s.height = 44
let str = "Jac"
str.append("ss")//error:Cannot use mutating member on immutable value: 'str' is a 'let' constant

let arr = [1,2,3]
arr[0] = 11//error:Cannot assign through subscript: 'arr' is a 'let' constant
arr.append(4)//error:Cannot use mutating member on immutable value: 'arr' is a 'let' constant

嵌套類型

struct Poker {
    enum Suit:Character {
        case spades = "??",hearts = "??",diamonds = "??",clubs = "??"
    }
    enum Rank:Int {
        case two = 2,three,four,five,six,seven,eight,nine,ten
        case jack,queen,king,ace
    }
}
print(Poker.Suit.spades)//spades
var suit = Poker.Suit.spades
suit = .diamonds
var rank = Poker.Rank.five
rank = .king

枚舉、結(jié)構(gòu)體兑障、類都可以定義方法

  • 一般把定義在枚舉侄非、結(jié)構(gòu)體蕉汪、類內(nèi)部的函數(shù),叫做方法
class Size {
    var width = 10
    var height = 10
    func show() {
        print("width = \(width),height = \(height)")
    }
}
let s = Size()
s.show()//width = 10,height = 10

struct Point {
    var x = 10
    var y = 10
    func show() {
       print("x = \(x),y = \(y)")
    }
}
let p = Point(x: 10, y: 10)
p.show()//x = 10,y = 10

enum PokerFace:Character {
    case spades = "??",hearts = "??",diamonds = "??",clubs = "??"
    func show() {
        print("face is \(rawValue)")
    }
}
let pf = PokerFace.hearts
pf.show()//face is ??
  • 方法不占用對(duì)象的內(nèi)存
  • 方法的本質(zhì)就是函數(shù)逞怨,方法者疤、函數(shù)都存放在代碼段

異變方法

Swift中classstruct都能定義方法,但是有一點(diǎn)區(qū)別的是默認(rèn)情況下叠赦,值類型屬性不能被自身的實(shí)例方法修改

struct Point {
    var x = 0.0,y = 0.0
    func moveBy(x deltaX:Double,y deltaY:Double)  {
        x += deltaX//error:Left side of mutating operator isn't mutable: 'self' is immutable
        y += deltaY//error:Left side of mutating operator isn't mutable: 'self' is immutable
    }
}
var p = Point()
p.moveBy(x:10.0, y:20.0)

想要修改驹马,需要在func前邊添加mutating,如下:

struct Point {
    var x = 0.0,y = 0.0
    mutating func moveBy(x deltaX:Double,y deltaY:Double)  {
        x += deltaX
        y += deltaY
    }
}
var p = Point()
p.moveBy(x:10.0, y:20.0)

異變方法的本質(zhì):對(duì)于異變方法除秀,傳入的self被標(biāo)記為inout參數(shù)糯累。無論在mutating方法內(nèi)部發(fā)生什么,都會(huì)影響外部依賴類型的一切册踩。

輸入輸出參數(shù):如果我們想函數(shù)能夠修改一個(gè)形式參數(shù)的值泳姐,而且希望這些改變?cè)诤瘮?shù)結(jié)束之后依然生效,那么就需要將形式參數(shù)定義為輸入輸出形式參數(shù)暂吉。在形式參數(shù)定義開始的時(shí)候在前邊添加一個(gè)inout關(guān)鍵字可以定義一個(gè)輸入輸出形式參數(shù)仗岸。

var age = 10
func modifyage(_ age:Int){
    age += 1//error:Left side of mutating operator isn't mutable: 'age' is a 'let' constant
報(bào)錯(cuò):因?yàn)楹瘮?shù)的形式參數(shù)都是let類型的常量
}

如果想內(nèi)部修改age,并且影響外部age值借笙,可以如下寫法
var age = 10
func modifyage(_ age:inout Int){
    age += 1
}
modifyage(&age)

方法調(diào)度

方式總結(jié):


影響函數(shù)派發(fā)方式

  • final:添加了final關(guān)鍵字的函數(shù)無法被重寫,使用靜態(tài)派發(fā)较锡,不會(huì)在vtable中出現(xiàn)业稼,且對(duì)objc運(yùn)行時(shí)不可見
  • dynamic:函數(shù)均可添加dynamic關(guān)鍵字,為非objc類和值類型的函數(shù)賦予動(dòng)態(tài)性蚂蕴,但派發(fā)方式還是函數(shù)表派發(fā)
  • @objc:該關(guān)鍵字可以將Swift函數(shù)暴露給Objc運(yùn)行時(shí)低散,依舊是函數(shù)表派發(fā)
  • @objc+dynamic:消息派發(fā)的方式

內(nèi)聯(lián)函數(shù)

函數(shù)內(nèi)聯(lián)是一種編譯器優(yōu)化技術(shù),它通過使用方法的內(nèi)容替換直接調(diào)用該方法骡楼,從而優(yōu)化性能

  • 將確保有時(shí)內(nèi)聯(lián)函數(shù)熔号。這是默認(rèn)行為,我們無需執(zhí)行任何操作鸟整,Swift編譯器可能會(huì)自動(dòng)內(nèi)聯(lián)函數(shù)作為優(yōu)化
  • always- 將確保始終內(nèi)聯(lián)函數(shù)引镊,通過在函數(shù)前添加@inline(__always)來實(shí)現(xiàn)此行為
  • never - 將確保永遠(yuǎn)不會(huì)內(nèi)聯(lián)函數(shù)。這可以通過在函數(shù)前添加@inline(never)來實(shí)現(xiàn)
  • 如果函數(shù)很長并且想避免增加代碼段大小篮条,請(qǐng)使用 @inline(never)
    如果對(duì)象只在聲明的文件中可見弟头,可以用private或fileprivate進(jìn)行修飾。編譯器會(huì)對(duì)private或fileprivate對(duì)象進(jìn)行檢查涉茧,確保沒有其他繼承關(guān)系的情形下赴恨,自動(dòng)打上final標(biāo)記,進(jìn)而使得對(duì)象獲得靜態(tài)派發(fā)的特性(fileprivate:只允許在定義的源文件中訪問伴栓,private:定義的聲明中訪問)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末伦连,一起剝皮案震驚了整個(gè)濱河市雨饺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌惑淳,老刑警劉巖额港,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異汛聚,居然都是意外死亡锹安,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門倚舀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叹哭,“玉大人,你說我怎么就攤上這事痕貌》缯郑” “怎么了?”我有些...
    開封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵舵稠,是天一觀的道長超升。 經(jīng)常有香客問我,道長哺徊,這世上最難降的妖魔是什么室琢? 我笑而不...
    開封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮落追,結(jié)果婚禮上盈滴,老公的妹妹穿的比我還像新娘。我一直安慰自己轿钠,他們只是感情好巢钓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著疗垛,像睡著了一般症汹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贷腕,一...
    開封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天背镇,我揣著相機(jī)與錄音,去河邊找鬼泽裳。 笑死芽世,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的诡壁。 我是一名探鬼主播济瓢,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼妹卿!你這毒婦竟也來了旺矾?” 一聲冷哼從身側(cè)響起蔑鹦,我...
    開封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎箕宙,沒想到半個(gè)月后嚎朽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡柬帕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年哟忍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陷寝。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡锅很,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出凤跑,到底是詐尸還是另有隱情爆安,我是刑警寧澤,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布仔引,位于F島的核電站扔仓,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏咖耘。R本人自食惡果不足惜翘簇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望儿倒。 院中可真熱鬧版保,春花似錦、人聲如沸义桂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽慷吊。三九已至,卻和暖如春曹抬,著一層夾襖步出監(jiān)牢的瞬間溉瓶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來泰國打工谤民, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留堰酿,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓张足,卻偏偏與公主長得像触创,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子为牍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

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