Swift語法 Swift5 【06 - 結構體和類】


  • 作者: Liwx
  • 郵箱: 1032282633@qq.com
  • 源碼: 需要源碼的同學, 可以在評論區留下您的郵箱

iOS Swift 語法 底層原理內存管理分析 專題:【iOS Swift5語法】

00 - 匯編
01 - 基礎語法
02 - 流程控制
03 - 函數
04 - 枚舉
05 - 可選項
06 - 結構體和類
07 - 閉包
08 - 屬性
09 - 方法
10 - 下標
11 - 繼承
12 - 初始化器init
13 - 可選項


目錄

  • 01-結構體
  • 02-結構體的初始化器
  • 03-思考下面代碼能通過么?
  • 04-自定義初始化器
  • 05-窺探初始化器的本質
  • 06-結構體內存結構
  • 07-類
  • 08-類的初始化器
  • 09-結構體與類的本質區別
  • 10-值類型
  • 11-值類型的賦值操作
  • 12-引用類型
  • 13-對象的堆空間申請過程
  • 14-引用類型的賦值操作
  • 15-值類型、引用類型的let
  • 16-嵌套類型
  • 17-枚舉、結構體、類都可以定義方法

01-結構體

  • 在Swift標準庫中,絕大多數的公開類型都是結構體,而枚舉只占很小一部分
  • 比如Bool、Int、Double、String、Array、Dictionary等常見類型都是結構體
  • 所有的結構體都有一個編譯器自動生成的初始化器(initializer, 初始化方法,構造器,構造方法)
  • 調用構造方法時,可以傳入所有成員值,用以初始化所有成員(存儲屬性, Stored Property)
struct Date {
    var year: Int
    var month: Int
    var day: Int
}
var date = Date(year: 2019, month: 6, day: 1)

02-結構體的初始化器

  • 編譯器會根據情況,可能會為結構體生成多個初始化器,宗旨是: 保證所有成員都有初始值

  • 示例1

// 結構體所有存儲屬性都沒有設置初始值
struct Point {
    var x: Int
    var y: Int
}

var p1 = Point(x: 10, y: 10)
//var p2 = Point(y: 10)   // 報錯: missing argument for parameter 'x' in call
//var p3 = Point(x: 10)   // 報錯: missing argument for parameter 'y' in call
//var p4 = Point()        // 報錯: missing arguments for parameters 'x', 'y' in call
  • 示例2
// 結構體部分存儲屬性沒有設置初始值
struct Point {
    var x: Int = 0
    var y: Int
}
var p1 = Point(x: 10, y: 10)
var p2 = Point(y: 10)
//var p3 = Point(x: 10)   // 報錯: missing argument for parameter 'y' in call
//var p4 = Point()        // 報錯: missing argument for parameter 'y' in call
  • 示例3
// 結構體部分存儲屬性沒有設置初始值
struct Point {
    var x: Int
    var y: Int = 0
}
var p1 = Point(x: 10, y: 10)
//var p2 = Point(y: 10)   // 報錯: missing argument for parameter 'x' in call
var p3 = Point(x: 10)
//var p4 = Point()        // 報錯: missing argument for parameter 'x' in call
  • 示例4
// 結構體所有存儲屬性都有設置初始值
struct Point {
    var x: Int = 0
    var y: Int = 0
}
var p1 = Point(x: 10, y: 10)
var p2 = Point(y: 10)   
var p3 = Point(x: 10)
var p4 = Point()

03-思考下面代碼能通過么?

  • 可選項都有個默認值nil
    • 因此可以編譯通過
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()

04-自定義初始化器

  • 一旦在定義結構體時自定義了初始化器,編譯器就不會再幫它自動生成其他初始化器
struct Point {
    var x: Int = 0
    var y: Int = 0
    init(x: Int, y: Int) {
        self.x = x
        self.y = y
    }
}
var p1 = Point(x: 10, y: 10)
//var p2 = Point(y: 10)   // error: missing argument for parameter 'x' in call
//var p3 = Point(x: 10)   // error: missing argument for parameter 'y' in call
//var p4 = Point()        // error: missing arguments for parameters 'x', 'y' in call

05-窺探初始化器的本質

  • 以下2段代碼完全等效
struct Point {
    var x: Int = 0
    var y: Int = 0
}
var p = Point()
struct Point {
    var x: Int
    var y: Int
    init() {
        x = 0
        y = 0
    }
}
var p = Point()
  • 通過匯編代碼對比,上面兩段代碼的匯編代碼一模一樣
06-結構體和類`Point.init():
->  0x100000bf0 <+0>:  pushq  %rbp
    0x100000bf1 <+1>:  movq   %rsp, %rbp
    0x100000bf4 <+4>:  xorps  %xmm0, %xmm0
    0x100000bf7 <+7>:  movaps %xmm0, -0x10(%rbp)
    0x100000bfb <+11>: movq   $0x0, -0x10(%rbp)
    0x100000c03 <+19>: movq   $0x0, -0x8(%rbp)
    0x100000c0b <+27>: xorl   %eax, %eax
    0x100000c0d <+29>: movl   %eax, %ecx
    0x100000c0f <+31>: movq   %rcx, %rax
    0x100000c12 <+34>: movq   %rcx, %rdx
    0x100000c15 <+37>: popq   %rbp
    0x100000c16 <+38>: retq

06-結構體內存結構

struct Point {
    var x: Int = 0
    var y: Int = 0
    var origin: Bool = false
}

print(MemoryLayout<Point>.size)     // 17
print(MemoryLayout<Point>.stride)   // 24
print(MemoryLayout<Point>.alignment)// 8


var p1 = Point(x: 10, y: 20, origin: true)
print(Mems.ptr(ofVal: &p1))         // 查看地址0x000000010000a6e8
// 0A 00 00 00 00 00 00 00
// 14 00 00 00 00 00 00 00
// 01 00 00 00 00 00 00 00

print(Mems.memStr(ofVal: &p1))      // 查看地址里面的內容 0x000000000000000a 0x0000000000000014 0x0000000000000001
image.png

07-類

  • 類的定義和結構體類似,但編譯器并沒有為類自動生成可以傳入成員值的初始化器
// 結構體
struct Point {
    var x: Int = 0
    var y: Int = 0
}

let p1 = Point()
let p2 = Point(x: 10, y: 10)
let p3 = Point(y: 10)
let p4 = Point(x: 10)
// 類
class Point {
    var x: Int = 0
    var y: Int = 0
}

let p1 = Point()
//let p2 = Point(x: 10, y: 10)    // error: argument passed to call that takes no arguments
//let p3 = Point(y: 10)   // error: argument passed to call that takes no arguments
//let p4 = Point(x: 10)   // error: argument passed to call that takes no arguments

  • 如果類成員未指定初始值,編譯器不會自動生成無參的初始化器
class Point {       // error: class 'Point' has no initializers
    var x: Int
    var y: Int
}
let p1 = Point()    // error: 'Point' cannot be constructed because it has no accessible initializers

08-類的初始化器

  • 如果類的所有成員都在定義的時候指定了初始值,編譯器會為類生成無參的初始化器
    • 成員的初始化是在這個初始化器中完成的

  • 以下2段代碼完全等效
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()

據匯編觀察, 2段代碼的匯編代碼一模一樣

06-結構體和類`Point.init():
    0x1000019e0 <+0>:   pushq  %rbp
    0x1000019e1 <+1>:   movq   %rsp, %rbp
    0x1000019e4 <+4>:   subq   $0x60, %rsp
    0x1000019e8 <+8>:   movq   $0x0, -0x8(%rbp)
    0x1000019f0 <+16>:  movq   %r13, -0x8(%rbp)
->  0x1000019f4 <+20>:  movq   %r13, %rax
    0x1000019f7 <+23>:  addq   $0x10, %rax
    0x1000019fb <+27>:  xorl   %ecx, %ecx
    0x1000019fd <+29>:  movl   %ecx, %edx
    0x1000019ff <+31>:  leaq   -0x20(%rbp), %rsi
    0x100001a03 <+35>:  movl   $0x21, %edi
    0x100001a08 <+40>:  movq   %rdi, -0x40(%rbp)
    0x100001a0c <+44>:  movq   %rax, %rdi
    0x100001a0f <+47>:  movq   %rsi, -0x48(%rbp)
    0x100001a13 <+51>:  movq   -0x40(%rbp), %rax
    0x100001a17 <+55>:  movq   %rdx, -0x50(%rbp)
    0x100001a1b <+59>:  movq   %rax, %rdx
    0x100001a1e <+62>:  movq   -0x50(%rbp), %rcx
    0x100001a22 <+66>:  movq   %r13, -0x58(%rbp)
    0x100001a26 <+70>:  callq  0x100005436               ; symbol stub for: swift_beginAccess
    0x100001a2b <+75>:  movq   -0x58(%rbp), %rax
    0x100001a2f <+79>:  movq   $0xa, 0x10(%rax)      ; 屬性x 賦值 10
    0x100001a37 <+87>:  movq   -0x48(%rbp), %rdi
    0x100001a3b <+91>:  callq  0x100005454               ; symbol stub for: swift_endAccess
    0x100001a40 <+96>:  movq   -0x58(%rbp), %rax
    0x100001a44 <+100>: addq   $0x18, %rax
    0x100001a48 <+104>: leaq   -0x38(%rbp), %rcx
    0x100001a4c <+108>: movq   %rax, %rdi
    0x100001a4f <+111>: movq   %rcx, %rsi
    0x100001a52 <+114>: movq   -0x40(%rbp), %rdx
    0x100001a56 <+118>: movq   -0x50(%rbp), %rax
    0x100001a5a <+122>: movq   %rcx, -0x60(%rbp)
    0x100001a5e <+126>: movq   %rax, %rcx
    0x100001a61 <+129>: callq  0x100005436               ; symbol stub for: swift_beginAccess
    0x100001a66 <+134>: movq   -0x58(%rbp), %rax
    0x100001a6a <+138>: movq   $0x14, 0x18(%rax)    ; 屬性y 賦值 20
    0x100001a72 <+146>: movq   -0x60(%rbp), %rdi
    0x100001a76 <+150>: callq  0x100005454               ; symbol stub for: swift_endAccess
    0x100001a7b <+155>: movq   -0x58(%rbp), %rax
    0x100001a7f <+159>: addq   $0x60, %rsp
    0x100001a83 <+163>: popq   %rbp
    0x100001a84 <+164>: retq 

09-結構體與類的本質區別

  • 結構體值類型(枚舉也是值類型), 引用類型(指針類型)
class Size {
    var width = 1
    var height = 2
}

struct Point {
    var x = 3
    var y = 4
}

func test() {
    var size = Size()   // size是指針變量,占用棧空間8個字節
    var point = Point() // point是結構體變量,占用棧空間16個字節
}
  • 下圖為64bit環境內存布局
QQ20200422-093229.png

  • 判斷是否在堆空間,查看匯編是否有調用alloc或者malloc
class Size {
    var width = 1
    var height = 2
}
struct Point {
    var x = 3
    var y = 4
}
var size = Size()          // __allocating_init, swift_allocObject, swift_slowAlloc, malloc
var point = Point()     // 查看匯編代碼未調用alloc或malloc
print(Mems.size(ofRef: size))   // 32, Mems.size(value)引用類型變量value占用的堆空間大小
print("MemoryLayout<Size>.stride", MemoryLayout<Size>.stride)   // 8, 在64bit環境引用類型占用棧空間內存始終是8
print("MemoryLayout<Point>.stride", MemoryLayout<Point>.stride) // 16
// size和point地址是??臻g,
print("size變量的地址", Mems.ptr(ofVal: &size))      // size變量的地址 0x00007ffeefbff040
print("size變量的內容", Mems.memStr(ofVal: &size))   // size變量的內容 0x000000010076ee50
print("size所指向內存的地址", Mems.ptr(ofRef: size))  // size所指向內存的地址 0x000000010076ee50
print("size所指向內存的內容", Mems.memStr(ofRef: size))// size所指向內存的內容 0x000000010000a500 0x0000000200000002 0x0000000000000001 0x0000000000000002
print("point變量的地址", Mems.ptr(ofVal: &point))    // point變量的地址 0x00007ffeefbff030
print("point變量的內存", Mems.memStr(ofVal: &point)) // point變量的內存 0x0000000000000003 0x0000000000000004

  • 在Mac、iOS中的malloc函數分配的內存大小總是16的倍數
import Foundation

var ptr1 = malloc(16)       // malloc函數在Foundation框架中,需import Foundation
print(malloc_size(ptr1))    // 16
var ptr2 = malloc(1)
print(malloc_size(ptr2))    // 16
var ptr3 = malloc(17)
print(malloc_size(ptr3))    // 32

10-值類型

  • 值類型賦值給var、let或者給函數傳參,是直接將所有內容拷貝一份
    • 類似于對文件進行copy、paste操作, 產生了全新的文件副本.屬于深拷貝(deep copy)
func testValueType() {
    struct Point {
        var x: Int
        var y: Int
    }
    
    var p1 = Point(x: 10, y: 20)
    var p2 = p1
    
    p2.x = 11
    p2.y = 22
    print(p2.x , p2.y)
}
testValueType()
  • 值類型深拷貝


    QQ20200422-101724.png
  • 匯編分析, p1先拷貝1份給p2,之后再對p2進行賦值操作

image.png

11-值類型的賦值操作

  • Swift標準庫中, 為了能提升性能, String、Array、Dictionary、Set采取了Copy On Write的技術
    • 比如僅當有"寫"操作時,才會真正執行拷貝操作
    • 對于標準庫值類型的賦值操作, Swift能確保最佳性能,所以沒必要為了保證最佳性能來避免賦值
  • 建議: 不需要修改的,盡量定義成let

  • 字符串
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]

  • 結構體賦值內存分析
struct Point {
    var x: Int
    var y: Int
}

var p1 = Point(x: 10, y: 20)
print(Mems.ptr(ofVal: &p1))     // 0x0000000107647780
print(Mems.memStr(ofVal: &p1))  // 0x000000000000000a 0x0000000000000014

p1 = Point(x: 11, y: 22)
print(Mems.ptr(ofVal: &p1))     // 0x0000000107647780
print(Mems.memStr(ofVal: &p1))  // 0x000000000000000b 0x0000000000000016
image.png

12-引用類型

  • 引用賦值給var、let或者給函數傳參,是將內存地址拷貝一份
    • 類似于指針一個文件的替身(快捷方式、鏈接), 指向的是同一個文件,屬于淺拷貝(shallow copy)
func testReferenceType() {
    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)
    var s2 = s1
    s2.width = 11
    s2.height = 22
    print(s1.width, s1.height)  // 11 22
    print(s2.width, s2.height)  // 11 22
}
testReferenceType()
image.png
QQ20200422-111917.png

13-對象的堆空間申請過程

  • 在Swift中,創建類的實例對象,要向堆空間申請內存,大概流程如下
    • Class.__allocating_init()
    • libswiftCore.dylib: swift_allocObject
    • libswiftCode.dylib: swift_slowAlloc
    • libsystem_malloc.dylib: malloc
  • 在Mac、iOS中的malloc函數分配的內存大小總是16的倍數
  • 通過class_getInstanceSize 可以得知: 類的對象至少需要占用多少內存
import Foundation

class Point {
    // 指向類型信息 8
    // 引用計數 8
    var x = 11      // 8
    var test = true // 1
    var y = 22      // 8
} // 33, 40, 48
var p = Point() // malloc函數分配的內存大小總是16的倍數, 所以堆空間分配48個字節
print(class_getInstanceSize(type(of: p)))   // 40
print(class_getInstanceSize(Point.self))    // 40 Point.self 相當于 [Point class] [p class]
print(Mems.size(ofRef: p))      // 48, 堆空間分配48個字節

14-引用類型的賦值操作

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)
print(Mems.ptr(ofVal: &s1))     // 0x0000000111fdca40
print(Mems.ptr(ofRef: s1))      // 0x000060000047d140
print(Mems.memStr(ofRef: s1))   // 0x0000000111fdc568 0x0000000200000002 0x000000000000000a 0x0000000000000014
s1 = Size(width:11, height: 22)
print(Mems.ptr(ofVal: &s1))     // 0x0000000111fdca40
print(Mems.ptr(ofRef: s1))      // 0x0000600000422e00
print(Mems.memStr(ofRef: s1))   // 0x0000000111fdc568 0x0000000400000002 0x000000000000000b 0x0000000000000016
image.png

15-值類型、引用類型的let

  • 值類型定義為let的實例, 不能修改值類型實例的成員屬性
  • 引用類型定義為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)         // let代表p變量的內存不可修改, p結構體變量占用16個字節
//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) // let代表s變量的內存不可修改 s指針變量占用8個字節
//s = Size(width: 11, height: 22)   // error: cannot assign to value: 's' is a 'let' constant
s.width = 33
s.height = 44

  • let修飾的字符串,不能對數組進行增刪改操作
// let 修飾的字符串 不允許使用append等賦值操作
let str = "Jack"
// str.append("_Rose")               // error: cannot use mutating member on immutable value
print(str)
  • let修飾的數組,不能對數組進行增刪改操作
// let修飾的數組,不能對數組進行增刪改操作
let arr = [1, 2, 3]
// arr[0] = 1              // error: cannot assign through subscript: 'arr' is a 'let' constant
// arr.append(4)           // error: cannot use mutating member on immutable value
print(arr)

16-嵌套類型

  • 嵌套類型的簡單使用
struct Poker {
    enum Suit : Character {
        case spades = "??"
        case hearts = "??"
        case diamonds = "??"
        case clubs = "??"
    }

    enum Rank : Int {
        case two = 2, three, four, five, six, seven, eight, nine, TernaryPrecedence
        case jack, queen, king, ace
    }
}

// 獲取嵌套類型的原始值
print(Poker.Suit.hearts.rawValue)   // ??

var suit = Poker.Suit.spades       
suit = .diamonds

var rank = Poker.Rank.five
rank = .king

17-枚舉、結構體、類都可以定義方法

  • 一般把定義在枚舉、結構體、類內部的函數,叫做方法
  • 方法占用對象的內存嗎?
    • 不占用
    • 方法的本質就是函數
    • 方法、函數都存放在代碼段

  • 類定義方法
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()
p.show()    // x = 10, y = 10
  • 枚舉定義方法
enum Poker : Character {
    case spades = "??"
    case hearts = "??"
    case diamonds = "??"
    case clubs = "??"
    func show() {
        print("face is \(rawValue)")
    }
}
let pf = Poker.hearts
pf.show()   // face is ??

  • 匯編分析方法、函數、全局變量、堆空間、局部變量(棧空間)內存分布
func show1() {
    print("show1")
}

class Point {
    var x = 11
    var y = 22
    func show() {
        var a = 10
        print("局部變量(棧空間)", Mems.ptr(ofVal: &a))
        print(x, y)
    }
}

var p = Point()
p.show()
show1()

print("全局變量", Mems.ptr(ofVal: &p))
print("堆空間", Mems.ptr(ofRef: p))
  • 方法、函數、全局變量、堆空間、局部變量(??臻g)內存區域分布分析
 Point.show:    0x100001740        (代碼區)
 show1:         0x100001290        (代碼區)
 
 全局變量        0x100007398        (全局區)
 堆空間          0x10053e5d0        (堆空間)
 局部變量(??臻g) 0x7ffeefbff3e8      (棧空間)

iOS Swift 語法 底層原理內存管理分析 專題:【iOS Swift5語法】

下一篇: 07 - 閉包
上一篇: 05 - 可選項


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
禁止轉載,如需轉載請通過簡信或評論聯系作者。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373