Swift快速入门(二)之 面向对象编程

Swift快速入门(二)之 面向对象编程

Swift快速入门(一)之基础语法

本文为第二篇《Swift面向对象编程》主要涵盖以下内容

  • 函数
  • 闭包
  • 枚举
  • 结构体和类
  • 属性
  • 初始化

函数

函数(function)相当于java中的方法。函数的的声明使用 func 关键字。

函数声明示例

var title = "Hello,函数"
//声明函数
func sayHell(){
    print(title)
}
//函数的调用
sayHell()

声明带参数的函数

func sayHello(name:String)
{
    print("Hello \(name)")
}
sayHello(name: "Taoto")

函数-外部参数

外部参数 让函数体外可见的参数名不同于内部的参数名。也就是说调用函数的时候用一个参数名字,在函数体内用另一个名字。

//外部调用使用参数名 为 to,内部使用参数名为 name
func sayHello(to name:String){
    print("外部参数的使用:Hello \(name)")
}
sayHello(to : "jake")
```swift
#### 函数-可变参数
1. 可变参数可接受 0个或多个值
2. 函数只能一个可变参数,且可变参数需作为最后一个参数
```swift
func sayHelloVariadic(to names : String...)
{
    print("Hello everyons:\(names)")
}
sayHelloVariadic(to: "jake","jerry","kobe","jordan")
sayHelloVariadic() //可变参数可接受0个值

函数-默认参数

  1. 默认参数可接受默认参数值;
  2. 默认参数值应该放在函数参数列表的末尾;
  3. 如果形参有默认值,则调用时可以省略实参;
func sayHelloDefault(name:String = "sir")
{
    print("Hello \(name)")
}
func hi(age:Int32, name:String = "sir")
{
    print("hi \(name),your age is \(age)")
}
sayHelloDefault()  //省略实参
sayHelloDefault(name: "god")
hi(age: 12)
hi(age: 22,name:  "mimo")

函数-inout参数

  1. inout参数能让函数影响函数体以外的变量
  2. 传递inout参数的变量名前需在前面加一个&
var erro = "401"
func appendErroCode(erroStr : inout String)
{
    erroStr += " erro code"
}
appendErroCode(erroStr: &erro)  //传递inout参数的变量名前需在前面加一个&
print(erro) 

函数-返回值

返回值语法: -> returnType

//声明一个加法函数,接收两个整数的参数,并返回它们的和
func addDescriptionFor(a:Int,b:Int) ->Int
{
    return a+b;
}
var sumNum = addDescriptionFor(a: 10, b: 18)
print("求和结果 \(sumNum)")

嵌套函数和作用域

嵌套函数在另一个函数内部声明并实现,并可以访问该函数内部的成员变量

//示例:通过底和高计算面积
func areaOfTraiangleWith(base:Double, height:Double) ->Double
{
    let numberator = base * height
    func divider() -> Double
    {
        return numberator/2;
    }
    return divider();
}
print(areaOfTraiangleWith(base: 9, height: 10))

函数-多个返回值

函数可以返回不止一个值,可以是元祖,或复杂对象

//使用元祖作为返回值
//示例代码,传入两个参数,分别将这两个参数乘2,再组装成一个元祖返回
func copyTwo(d1:Double ,d2:Double) ->(n1:Double,n2 :Double)
{
    return (d1*2,d2*2)
}
var  result = copyTwo(d1: 3, d2: 4)
print(result)

函数-可空的返回值类型

func funcNil(targetStr:String?) -> String?
{
    return targetStr
}
print("可空的返回值类型 \(funcNil(targetStr: nil))")

提前退出函数

guard语句 中文 “卫语句”,可提前退出函数,防止不当条件下运行的方式

func sayHello(name : String, age:Int)
{
    if age<0 || age>100
    {
        return
    }
    
    print("hello \(name),age is \(age)")
}
sayHello(name: "leo", age: 18)

闭包 closure

闭包是完成特定任务的功能组,可以理解为无名字的函数。

  1. swift中闭包可以作为参数,也可以作为返回值
  2. 闭包和函数能捕获在其作用域中定义的变量
  3. 闭包是引用类型

==ps==:闭包和java中lambda表达式匿名函数类似,甚至可以理解为同一个东西。

闭包语法表达式

{(parameter) -> return type in
    //表达式
}

通过一个对整数数组进行排序对闭包特性进行学习

var ages = [1,99,78,24,88,61,56,12,44,39]
func sortAscending(_ i:Int , _ j:Int) ->Bool
{
    return i < j
}
var agesSorted = ages.sorted(by: sortAscending)
//print(agesSorted)

//闭包表达式对排序进行简化
agesSorted = ages.sorted(by: {
    (i:Int , j:Int) ->Bool in
    return i<j
})

//利用类型推断的闭包语法
agesSorted = ages.sorted(by:{ i,j in i<j })


//利用参数的快捷语法
//使用$0表示第一个参数的值,$1表示第二个参数的值
//现在内联闭包表达式利用了快捷参数语法,就不需要像之前申明i和j那样显示声明参数了
agesSorted = ages.sorted(by: {$0 < $1})

函数作为返回值

//声明一个getSum函数,其返回值为 为一个
//(接收两个Int参数,返回一个Int返回值的函数)
func getSum() -> (Int,Int) -> Int{
    func sumTotal(a:Int , b:Int) -> Int{
        return a + b;
    }
    return sumTotal
}
var sumF = getSum()
print(sumF(8,9))

函数作为参数

//contion为 接受一个Int参数返回Bool类型 的闭包
func isBiggerThanZero(num:Int, condition :(Int) -> Bool) -> String
{
    if(condition(num))
    {
        return "大于0的整数!!!"
    }else{
        return "小于0的整数"
    }
}

var result = isBiggerThanZero(num: 9, condition: {
    (i : Int ) -> Bool in
    return i>0 }
)
print(result)

//利用类型推断进行简化 闭包
result = isBiggerThanZero(num: -9, condition: {
    i in i>0
})
print(result)
//利用快捷表达式再次进行简化
result = isBiggerThanZero(num: 10, condition: {$0 > 0})
print(result)

函数式编程--高阶函数

map 变换数组的内容

把数组的内容从一个值变成另外一个值,并把这些新值放进一个新数组返回

let projectPopulations = [1024,2048,4096]
let projectedPopulations = projectPopulations.map {
    (population : Int) -> Int in
    return population * 2
}
print(projectedPopulations)
//闭包的简写
let projectedPopulations3 = projectPopulations.map {
    i in i*3
}
//将整数数组变为每个元素依次乘以4 的数组
print("闭包的简写 \(projectedPopulations3)")
let projectedPopulations4 = projectPopulations.map {
     $0 * 4
}
//将整数数组变为Bool类型数组
print("4闭包的简写 \(projectedPopulations4)")
let projectedPopulations2 = projectPopulations.map{
    (i : Int) -> Bool in
    return i>10
}
print(projectedPopulations2)

filter 过滤数组

结果数组会包含原数组满足条件的值

var bigProjectedPopulations = projectPopulations.filter {
    (population:Int) -> Bool in
    return population>2000 //当population>2000时 返回true
}
//print(bigProjectedPopulations)
bigProjectedPopulations = projectPopulations.filter({
    (i:Int) -> Bool in
    return i > 10
})
print("bigProjectedPopulations2 :\(bigProjectedPopulations)")
//简写,利用条件推断
bigProjectedPopulations = projectPopulations.filter({
    i in i > 0
})
//print("bigProjectedPopulations3 :\(bigProjectedPopulations)")
//利用参数的快捷语法
bigProjectedPopulations = projectPopulations.filter({
    $0 > 0
})
print("bigProjectedPopulations4 :\(bigProjectedPopulations)")
//尾部闭包
bigProjectedPopulations = projectPopulations.filter{ $0 > 0}
print("bigProjectedPopulations5 尾部闭包:\(bigProjectedPopulations)")

reduce 求和

对数组中各个元素遍历求和

//let projectPopulations = [1024,2048,4096]
var numberSum = projectPopulations.reduce(0, { x, y in
    x + y
})
print(numberSum)
numberSum = projectPopulations.reduce(0, {$0 + $1})
print("j \(numberSum)")

枚举

声明枚举

enum TextAlignment{
    case left
    case right
    case center
}

创建一个枚举实例

//第一次创建枚举变量时,必须指定枚举名和值
var alignment = TextAlignment.center
alignment = .left
print(alignment)

比较枚举时

if(alignment == .left)
{
    print("The alignment is \(alignment)")
}

枚举在switch的使用

==注意==:枚举作为switc语句case时,建议不使用default,编译器会检测case是否全部覆盖,若覆盖不全则编译报错

switch alignment{
case .left:
    print("left align")
case .center:
    print("center align")
case .right:
    print("right align")
}

原始枚举值 rawValue

目前支持:Int、Float、Double、String

//若原始值未赋值,则从0..1依次递增
enum TextAlignmentInt : Int
{
    case left = 10
    case center = 20
    case right = 30
}
print(TextAlignmentInt.left.rawValue)

原始值转回枚举

let myRawValue = 20
if let myAlignment = TextAlignmentInt(rawValue: myRawValue)
{
    print("successful converted \(myRawValue) into \(myAlignment)")
}else{
    print("failure converted \(myRawValue) into TextAlignmentInt")
}

创建带字符串原始值的枚举

enum ProgramingLanguage :String
{
    case swift = "swift"
    case java = "java"
    case other  //若未赋值则rawValue = others
}
var language = ProgramingLanguage.swift
print(language.rawValue)
language = .other
print(language.rawValue)

结构体 和 类

结构体(strut)是把相关数据块组合在一起的一种类型;结构体是值类型。

声明结构体 strut

struct Town { }

结构体内添加属性

struct Town {
    //存储属性,可以有默认值
    var name = "温泉小镇"  //名称
    var population = 2300 //人口
    }

结构体实例方法

struct Town {
    var name = "温泉小镇"  //名称
    var population = 2300 //人口
    
    func sayHello() {
        print("Hello! My name is \(name) ,population is \(population)")
    }
}

结构体 mutating 方法

结构体是值类型,需要在修改实例属性的方法前加上mutating关键字。

struct Town {
    //存储属性,可以有默认值
    var name = "温泉小镇"  //名称
    var population = 2300 //人口
    
//由于stuct是值类型,修改其属性内容需使用 mutating
    mutating func addPopulation(amount : Int) {
        population += amount
    }

结构体 类型方法(static方法)

类型方法用static关键字进行修饰,可通过类型本身进行调用(即java中的静态方法)

struct Town {
    //stuct的 类型方法static修饰
    static func numberOfSteet() -> Int
    {
        return 10
    }
}

//类型方法的调用
Town.numberOfSteet()

创建结构体实例

var myTown = Town()
print("Town name is \(myTown.name),population is \(myTown.population)")
myTown.sayHello() 
myTown.addPopulation(amount: 1000)
myTown.sayHello()
print(Town.numberOfSteet())

类 class

类和结构体类似,但类是引用类型。

类的声明

示例:类的声明,包含属性、对象方法

  1. 父类中普通对象方法可以被子类重写override
  2. 父类中使用final修饰的对象方法子类不能重写override
class Monster {
    var town: Town?
    var name = "Monster"
    
    func terrorizeTown() {
        if town != nil
        {
            print("\(name) terrorizing a town: \(town?.name)!")
        }else
        {
            print("\(name) hasn't found a town to terrorize yet...")
        }
    }

类的 类方法

  1. 类方法可以用class或static修饰
  2. class修饰的类方法子类可以override
  3. static修饰的类方法子类不可重写(和java一致)

类的继承 子类方法override

子类继承父类的属性和方法。

import Foundation
//声明类 怪物
class Monster {
    var town: Town?
    var name = "Monster"
    
    func terrorizeTown() {
        if town != nil
        {
            print("\(name) terrorizing a town: \(town?.name)!")
        }else
        {
            print("\(name) hasn't found a town to terrorize yet...")
        }
    }
    
    //final方法 ,子类中无法ovveride
    final func hello(name :String)  {
        print("hello \(name)")
    }
    
    
    //类的 类型方法class修饰,即java中的静态方法,子类可以继承
    class func numberOfMonster() -> Int
    {
        return 10
    }
    
    //类的 类型方法class修饰,即java中的静态方法,子类可以继承
    //当类型方法 前添加 final或static修饰时,则子类不可override
    final class func makeNoise() -> String
    {
        return "Ping Pong"
    }
}

//声明类 僵尸继承自 Monster
class Zombie: Monster {
    var walkWithLimp = true
    
    //复写父类方法
    override func terrorizeTown() {
        town?.addPopulation(amount: -10)  //人口减10
        super.terrorizeTown()
    }
    
    //在swift中子类可以override 父类的 类型方法
    override class func numberOfMonster() -> Int
    {
        return 1
    }
}

属性 class

属性将值跟特定的类、结构或枚举关联。
属性可分为存储属性和计算属性:

  1. 存储属性:存储常量或变量作为实例的一部分,用于类和结构体
  2. 计算属性:计算一个值,用于类和结构体及枚举

声明存储属性

class People {
    //存储属性可以是常量或变量,可以有默认初始值
    let no = 0
    var age:Int
    var name:String
}

声明惰性存储属性

惰性存储属性当第一次访问的时候被调用,且只会被计算一次

//声明懒加载属性
//当第一次访问的时候被调用
//只会被计算一次
lazy var personType:Classification = {
    switch age{
    case 0...12:
        return Classification.Child
    case 13...18:
        return Classification.Teenagers
    case let age where(age>18):
        return Classification.Adult
    default:
        return Classification.Ohter
    }
}()

嵌套类型

嵌套类型是定义在一个类型内部的类型,类似为Java中的内部类

// 属性的使用
class People {
    //存储属性可以是常量或变量,可以有默认初始值
    let no = 0
    var age:Int
    var name:String
    
    init(age:Int,name:String) {
        self.age = age
        self.name = name
    }
    
    //内部类型
    enum Classification {
        case Child
        case Teenagers
        case Adult
        case Ohter
    }
}

只读属性

只读属性不允许进行赋值操作

//声明只读属性
var victimPool:Int{
    get{
        return 9
    }
}

声明计算属性

计算属性不能直接设置变量值,只能根据读取方法中定义的计算获取一个返回值,每次获取都是动态实时的。

//声明计算属性:不能直接设置变量值,只能根据读取方法中定义的计算获取一个返回值
var personTypeDynamic: Classification {
    get{
        switch age{
        case 0...12:
            return Classification.Child
        case 13...18:
            return Classification.Teenagers
        case let age where(age>18):
            return Classification.Adult
        default:
            return Classification.Ohter
        }
    }
}

类型属性

类型属性 类似java中的静态属性,但区别在于:

  1. strut中 类型属性使用static关键进行声明
  2. class中,类型属性可使用static和class进行声明;static声明的类型属性子类不可以override,而class声明的类型属性可以被子类override
class People {
    //static声明的属性变量
    static let height = 10
    static var height2 = 11
    class var width: Int {
        return 12
    }
}

class Man: People {
    //class声明的类型属性可以被override
    override class var width: Int {
        return 33
    }
}

初始化 initializer

Swift中初始化方法相当于java中的构造函数
1.在类和结构体中编译器会默认提供空的初始化方法
2.当用户新增带参数初始化方法后,编译器则不提供默认午餐初始化方法,如需使用要自行实现

struct Zoo {
    var name = ""
    var area: Double = 100
    
    
    //初始化方法、结构体
    init(name :String) {
        //支持初始化方法的重载
        self.init(name: name, area: 90)
    }
    
    init(name : String ,area :Double) {
        self.area = area
//        self.init(name: name)
        self.name = name
    }
}

委托初始化

对类型其他初始化方法进行调用,相当于java中的重载构造函数

//初始化方法、结构体
init(name :String) {
    //支持初始化方法的重载,对其他初始化方法进行调用
    self.init(name: name, area: 90)
}

init(name : String ,area :Double) {
    self.area = area
//        self.init(name: name)
    self.name = name
}

类初始化

类初始化通用语法和值类型类似,但由于类支持继承,会在值类型初始化的基础上新增了 指定(designated)初始化方法和便捷(convenience)初始化,类初始化方法一定是二者之一。

  • 指定初始化需要确保初始化完成前所有的属性都有值,以便实例可用。
  • 便捷初始化方法是指定初始化方法的补充,通过调用所在类的指定初始化方法来实现,主要作用是为某种特殊目的创建实例。
class Animal {
    var name:String
    var age:Int
    
    //指定初始化方法,不加任何修饰符
//1.需要确保初始化完成前所有的属性都有值,以便实例可用
    init(name:String,age:Int) {
        self.name = name
        self.age = age
    }
    
    //初始化、构造函数
    //△convenience 便捷初始化
    //是指定初始化方法的补充,通过调用所在类的指定初始化方法来实现
    convenience init(name:String) {
        self.init(name: name, age: 18)
    }
}

初始化方法自动继承

只有满足以下两种场景,子类会继承父类的初始化方法

  1. 如果子类没有定义任何指定初始化方法,就会继承父类的指定初始化方法。
  2. 如果子类实现了父类的所有指定初始化方法(无论通过显示还是隐式继承),就会继承父类的所有便捷初始化方法。

指定初始化方法创建实例

class Panda: Animal {
//子类没有定义任何指定初始化方法,就会继承父类的指定初始化方法
}

//调用如下
var p1:Panda = Panda.init(name: "tuantuan", age: 2)
print(p1.name)

子类新增指定初始化方法,在确保所有属性都有初始值时,则父类的初始化方法不在可以调用

class Panda: Animal {
    var weight :Float
    
    //子类新增指定初始化方法,在确保所有属性都有初始值时,则父类的初始化方法不在可以调用
    init(name: String, age: Int,weight:Float) {
        self.weight = weight
        super.init(name:name,age:age)
    }
}

//调用如下
var p1:Panda = Panda.init(name: "tuantuan", age: 2,weight: 19)
print(p1.name)

便捷初始化方法创建实例

由于便捷初始化方法必需要调用到指定初始化方法,一个类的便捷初始化方法和指定初始化方法会形成一个路径,类的存储属性通过这条路劲收到初始值
swift

class Panda: Animal {
    var weight :Float
    
    init(name: String, age: Int,weight:Float) {
        self.weight = weight
        super.init(name:name,age:age)
    }
    
    //声明一个便捷初始化方法
    convenience init(weight:Float) {
        self.init(name: "Panda_007", age: 2, weight: weight)
    }
}

//使用便捷初始化方法进行实例的初始化
let p2 = Panda.init(weight: 99)
print("name is \(p2.name),weight is \(p2.weight)")

类的必需初始化方法 required

使用required关键字声明的初始化方法,表示所有子类都必需提供这个初始化方法。跟覆盖继承自父类的其他方法不同,必需初始化方法不需要用override关键字标记,required标记已经隐含了覆盖的意思。

class Animal {
    var name:String
    var age:Int

    //必需子类实现的初始化方法
    required init(newname:String, age:Int) {
        self.age = age
        self.name = newname
    }
}

class Panda: Animal {
    required init(newname: String, age: Int) {
        name = newname
        self.age = age
    }
    
}

反初始化 deinit

即在类实例没用之后将其清除内存的过程;反初始化使用deinit表示,没有参数。

class Panda: Animal {
    //声明反初始化方法
    deinit {
        print("Panda is no longer with us")
    }
}

//调用:当实例为空,反初始化被触发调用
var p3:Panda? = Panda.init(newname: "mimi", age: 2)
print("name is \(p3?.name),weight is \(p3?.age)")
p3 = nil //实例为空,反初始化被触发调用

可失败的初始化方法 init?

可失败的初始化方法会返回可空实例

class Pig:Animal
{
    var color:String
    
    //声明失败的初始化方法
    init?(name: String, age: Int,color:String)
    {
        guard color != "" else
        {
            //f当不满足条件则返回nil,表示初始化失败
            return nil
        }
        self.color = color
        super.init(name: name, age: age)
    }
}
//调用
//可失败的初始化方法调用,
//当调用失败,初始化值为nil
var pig = Pig.init(name: "pig_9", age: 1, color: "")
print(pig)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容

  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,737评论 2 9
  • 123.继承 一个类可以从另外一个类继承方法,属性和其他特征。当一个类继承另外一个类时, 继承类叫子类, 被继承的...
    无沣阅读 1,383评论 2 4
  • 官方文档 初始化 Initialization是为准备使用类,结构体或者枚举实例的一个过程。这个过程涉及了在实例里...
    hrscy阅读 1,133评论 0 1
  • Hello Word 在屏幕上打印“Hello, world”,可以用一行代码实现: 你不需要为了输入输出或者字符...
    restkuan阅读 3,169评论 0 6
  • 女人我见过不少,这个陈圆圆,长得不算差。 姝姝婉婉,盈盈冉冉,莺声呖呖,六马仰秣。 老吴捧来剥好的瓜子仁:“大人您...
    扉页留白阅读 3,750评论 50 128