类与结构体

上一篇:函数
当前篇:类与结构体
下一篇:协议

这节课我们来学习最重要的一个知识点: ,之前学习的都是编程语言本身提供的最基础的类型,光使用这些类型很难完成更加抽象的任务,加入我们要抽象出 的类型,那么就需要自己动手定义一个新的 Class

我们暂时抛弃 Playground 这种教学开发方式,使用完成的 Project 工程来开发,打开 Xcode ,点击 File->New->Project,勾选 iOS 、Single View App,点击 Next ,输入工程名称 HelloWorld,开发语言选择 Swift ,点击 Next ,选择存放项目文件的目录,点击 Create ,一个新的项目就创建好了

工程配置文件

工程目录和配置文件如上图所示,将 Deployment Target 调整为 9.0,这表示我们的应用仅支持iOS9.0及以上,Devices 选择 iPhone ,这个应用就可以在苹果手机上面运行了,如果不方便在真机上运行,可以在虚拟机上面查看运行结果,在顶部偏左的地方选择iPhone X虚拟机,然后点击小三角开始运行,也可以用快捷键 Command + R

稍等片刻,我们的iPhone X虚拟机就启动完成了,并且 HelloWorld 应用已经安装在虚拟机上面运行了,目前还是一个空白的应用,什么都没有,接下来我们把能看见的 Hello World 显示在界面上,打开 ViewController.swift 文件并修改代码如下

import UIKit

class ViewController: UIViewController {
    
    let label = UILabel()   // 创建一个用于显示文本的 UILabel

    override func viewDidLoad() {
        super.viewDidLoad()
        //设置文本内容
        label.text = "Hello World!"
        //计算文本内容大小
        label.sizeToFit()
        //将文本添加到视图上
        view.addSubview(label)
    }
    
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        //将文本内容居于屏幕中央
        label.center = CGPoint.init(x: view.frame.width / 2, y: view.frame.height / 2)
    }

}

运行应用,我们就能看到屏幕中央的 Hello World!

Hello World

先不用急着去理解代码,我们还没有开始学用户界面开发,这里只是让新手看到用户界面的效果,同时引出我们教学过程中的程序入口,记得之前用 Playground 的时候,写完代码就能看见运行结果,但是真实的工程中不一样,程序需要一个开始运行的入口,我们可以把 ViewController 的 viewDidLoad 函数作为入口,只需要知道,当程序启动的时候会创建一个 ViewController 的实例用于显示用户界面,并调用实例的 viewDidLoad 函数

暂时忽略 ViewController 这个类,只在其中做一个入口

import UIKit

func lessonRun() {
    print("Hello World!")
}

class ViewController: UIViewController {
    
    let label = UILabel()   // 创建一个用于显示文本的 UILabel

    override func viewDidLoad() {
        super.viewDidLoad()
        //设置文本内容
        label.text = "Hello World!"
        //计算文本内容大小
        label.sizeToFit()
        //将文本添加到视图上
        view.addSubview(label)
        lessonRun()
    }
    
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        //将文本内容居于屏幕中央
        label.center = CGPoint.init(x: view.frame.width / 2, y: view.frame.height / 2)
    }

}

自定义类

现在我们就把 lessonRun 函数当做 Playground 吧,回到正题,我们希望自己来定义一个 的类型,为了方便代码管理,在项目文件中新建一个文件加专门用来存放相关联的文件

右键点击 HelloWorld 文件夹,点击 Add Files to ,然后点击左下角的 New Folder ,输入文件夹名字 ClassLesson ,然后点击右下角的 Add 按钮,一个文件夹就创建好了

开发实际项目的时候,我们会创建很多很多的 .swift 代码文件,而每一个文件基本上就是一个类型的定义,因此合理的用文件夹来进行分类是一个很有必要的习惯

右键点击 ClassLesson 文件夹,点击 New File ,勾选 iOS 、Swift File,点击 Next,输入文件名 Person 后点击 Create ,现在我们在 ClassLesson 文件夹下面创建了一个代码文件专门用来定义 Person

class Person {
    
}

定义类的方式就是 class + 类名 + { },类名的命名规则采用首字母大写的方式,并且最好有一个前缀名,因为在编程的过程中可能会用到别人写的代码,就很有可能出现类名冲突,前缀能避免绝大部分冲突,并且前缀也充当一个标记,你可以使用自己的昵称缩写作为前缀,比如 ZQPerson ,教学中我们就暂时不用前缀了,只要类名能大致表名意图就行了

存储属性

Person 这个类型我们首先想到的是,应该有身份证号,姓名,籍贯,年龄,性别这些基本属性

enum Gender {
    case unknown
    case male
    case female
}

class Person {
    var id: String = ""
    var name: String = ""
    var hometown: String = ""
    var age: Int = 0
    var gender: Gender = .unknown
}

有了这样的定义我们就可以开始创建一个 Person 类的实例了,修改lessonRun 函数如下

func lessonRun() {
    let person = Person.init()
    person.id = "1234567890"
    person.name = "JiangMing"
    person.hometown = "ChongQing"
    person.age = 25
    person.gender = .male
    //以上代码创建了一个人的实例,并设置了其基本属性
    print("My name is \(person.name) and I am \(person.age) years old")
}

功能函数

我们可以为类定义一些特有的功能,例如可以赋予 Person 类自我介绍的能力

enum Gender {
    case unknown
    case male
    case female
}

class Person {
    var id: String = ""
    var name: String = ""
    var hometown: String = ""
    var age: Int = 0
    var gender: Gender = .unknown
    
    /** 自我介绍*/
    func makeIntroduction() {
        print("My name is \(name)")
        print("I'm from \(hometown) and I am \(age) years old")
    }
    
}
func lessonRun() {
    let person = Person.init()
    person.id = "1234567890"
    person.name = "JiangMing"
    person.hometown = "ChongQing"
    person.age = 25
    person.gender = .male
    //以上代码创建了一个人的实例,并设置了其基本属性
    person.makeIntroduction()
}

构造函数

类的实例由构造函数创建出来,默认的构造函数是 init ,我们可以自己修改构造函数

设想一下,人的身份证号和家乡这样的属性应该在创建的时候就指定,并且不允许修改,最合理的方式就是把它们定义为常量,而类的常量属性必须在构造函数中初始化

enum Gender {
    case unknown
    case male
    case female
}

class Person {
    
    let id: String
    var name: String = ""
    let hometown: String
    var age: Int = 0
    var gender: Gender = .unknown
    
    /** 自定义构造函数*/
    init(id: String, hometown: String) {
        //当参数名与自身的属性名冲突时,使用self表示自己,没有参数名冲突时可以省略self
        self.id = id
        self.hometown = hometown
    }
    
    /** 自我介绍*/
    func makeIntroduction() {
        print("My name is \(name)")
        print("I'm from \(hometown) and I am \(age) years old")
    }
    
}
func lessonRun() {
    let person = Person.init(id: "1234567890", hometown: "ChongQing")
    person.name = "JiangMing"
    person.age = 25
    person.gender = .male
    //以上代码创建了一个人的实例,并设置了其基本属性
    person.makeIntroduction()
}

构造函数跟普通函数是区别的,它是用来创建实例的特殊函数,我们习惯用实例来统称创建出来的实体,也可以把 的实例叫做 对象 ,但是像字符串,整数这样的实例是不能叫做 对象

构造函数还可以有很多个

class Person {
    
    let id: String
    var name: String = ""
    let hometown: String
    var age: Int = 0
    var gender: Gender = .unknown
    
    /** 自定义构造函数*/
    init(id: String, hometown: String) {
        //当参数名与自身的属性名冲突时,使用self表示自己,没有参数名冲突时可以省略self
        self.id = id
        self.hometown = hometown
    }
    
    /** 构造函数可以有多个*/
    init(id: String, hometown: String, name: String) {
        self.id = id
        self.hometown = hometown
        self.name = name
    }
    
    /** convenience 修饰的叫便利构造函数,便利构造函数必须调用一个非便利构造函数*/
    convenience init(id: String, hometown: String, name: String, gender: Gender) {
        self.init(id: id, hometown: hometown, name: name)
        self.gender = gender
    }
    
    /** 自我介绍*/
    func makeIntroduction() {
        print("My name is \(name)")
        print("I'm from \(hometown) and I am \(age) years old")
    }
    
}

构造函数有许多许多规则讲究,在实际开发中遇到问题的时候可以根据错误提示去一步步完善知识点

析构函数

对象在使用完成后会被自动释放销毁,在对象被释放的时候它可能占用了系统资源需要我们手动去释放资源占用,因此我们需要知道对象何时被销毁,这就需要用到析构函数

class Person {
    
    let id: String
    var name: String = ""
    let hometown: String
    var age: Int = 0
    var gender: Gender = .unknown
    
    /** 自定义构造函数*/
    init(id: String, hometown: String) {
        //当参数名与自身的属性名冲突时,使用self表示自己,没有参数名冲突时可以省略self
        self.id = id
        self.hometown = hometown
    }
    
    /** 构造函数可以有多个*/
    init(id: String, hometown: String, name: String) {
        self.id = id
        self.hometown = hometown
        self.name = name
    }
    
    /** convenience 修饰的叫便利构造函数,便利构造函数必须调用一个非便利构造函数*/
    convenience init(id: String, hometown: String, name: String, gender: Gender) {
        self.init(id: id, hometown: hometown, name: name)
        self.gender = gender
    }
    
    /** 析构函数*/
    deinit {
        print("\(name) 的生命周期结束,被自动销毁")
    }
    
    /** 自我介绍*/
    func makeIntroduction() {
        print("My name is \(name)")
        print("I'm from \(hometown) and I am \(age) years old")
    }
    
}

属性观察器

假如当我们的年龄长一岁时,需要作出一些反应,我们可以用属性观察器来实现

    var age: Int = 0 {
        willSet(newAge) {
            print("我现在 \(age) 岁")
            print("我马上 \(newAge) 岁")
        }
        didSet(oldAge) {
            print("我刚刚 \(oldAge) 岁")
            print("我现在 \(age) 岁")
        }
    }
func lessonRun() {
    let person = Person.init(id: "1234567890", hometown: "ChongQing")
    person.name = "JiangMing"
    person.age = 25
    person.gender = .male
    //以上代码创建了一个人的实例,并设置了其基本属性
    person.age += 1
}

属性观察器有两个, willSet() 观察器观察属性改变之前的状态并捕捉新值,didSet() 观察器观察属性改变之后的状态并捕捉旧值,这两个观察器使用一个就够了,通常我们使用简写方式

    var age: Int = 0 {
        willSet {
            print("我现在 \(age) 岁")
            print("我马上 \(newValue) 岁")
        }
        didSet {
            print("我刚刚 \(oldValue) 岁")
            print("我现在 \(age) 岁")
        }
    }

属性观察器中 默认使用 newValue 表示新值,oldValue 表示旧值

计算属性

还有一种属性叫做计算属性,顾名思义,计算属性不是实体存在的属性,而是在需要使用的时候去动态计算的属性,比如说我们需要给 Person 类新增一个 的属性,如果把这两个新增属性设计成存储属性,显然是不合理了,因为已经有了 姓名 这样的存储属性, 只需要通过 姓名 来动态计算就行了

class Person {
    
    let id: String
    var name: String = ""
    let hometown: String
    var age: Int = 0
    var gender: Gender = .unknown
    
    /** 获取姓氏*/
    var familyName: String? {
        if name.isEmpty {
            return nil
        }
        //截取第一个字符
        return String(name.first!)
    }

    /** 获取名*/
    var givenName: String? {
        if name.isEmpty {
            return nil
        }
        //去掉第一个字符
        return String(name.dropFirst())
    }
    
    /** 自定义构造函数*/
    init(id: String, hometown: String) {
        //当参数名与自身的属性名冲突时,使用self表示自己,没有参数名冲突时可以省略self
        self.id = id
        self.hometown = hometown
    }
    
    /** 构造函数可以有多个*/
    init(id: String, hometown: String, name: String) {
        self.id = id
        self.hometown = hometown
        self.name = name
    }
    
    /** convenience 修饰的叫便利构造函数,便利构造函数必须调用一个非便利构造函数*/
    convenience init(id: String, hometown: String, name: String, gender: Gender) {
        self.init(id: id, hometown: hometown, name: name)
        self.gender = gender
    }
    
    /** 自我介绍*/
    func makeIntroduction() {
        print("My name is \(name)")
        print("I'm from \(hometown) and I am \(age) years old")
    }
    
}

这种方式只适用于汉语单姓,不够严谨,仅用于说明计算属性的特点

这样的计算属性叫做只读计算属性,因为我们只能通过这它来获取结果,不能通过它来设置新的值,如果我们希望能够设置 或者 来设置姓名 ,就得把 只读计算属性 改为 读写计算属性

class Person {
    
    let id: String
    var name: String = ""
    let hometown: String
    var age: Int = 0
    var gender: Gender = .unknown
    
    /** 姓氏*/
    var familyName: String? {
        get {
            if name.isEmpty {
                return nil
            }
            //截取第一个字符
            return String(name.first!)
        }
        set {
            if newValue != nil && !name.isEmpty {
                name = newValue! + givenName!
            }
        }
    }

    /** 名*/
    var givenName: String? {
        get {
            if name.isEmpty {
                return nil
            }
            //去掉第一个字符
            return String(name.dropFirst())
        }
        set {
            if newValue != nil && !name.isEmpty {
                name = familyName! + newValue!
            }
        }
    }
    
    /** 自定义构造函数*/
    init(id: String, hometown: String) {
        //当参数名与自身的属性名冲突时,使用self表示自己,没有参数名冲突时可以省略self
        self.id = id
        self.hometown = hometown
    }
    
    /** 构造函数可以有多个*/
    init(id: String, hometown: String, name: String) {
        self.id = id
        self.hometown = hometown
        self.name = name
    }
    
    /** convenience 修饰的叫便利构造函数,便利构造函数必须调用一个非便利构造函数*/
    convenience init(id: String, hometown: String, name: String, gender: Gender) {
        self.init(id: id, hometown: hometown, name: name)
        self.gender = gender
    }
    
    /** 自我介绍*/
    func makeIntroduction() {
        print("My name is \(name)")
        print("I'm from \(hometown) and I am \(age) years old")
    }
    
}
func lessonRun() {
    let person = Person.init(id: "1234567890", hometown: "ChongQing")
    person.name = "姜小明"
    person.age = 25
    person.gender = .male
    print(person.name)
    person.familyName = "张"
    person.givenName = "小琦"
    print(person.name)
}

类的继承

如果我们现在要定义一个 教师 的类型,会遇到一个问题,如果从头开始定义,就需要把 身份证,名字,家乡等等一切属性和功能全部重新定义一遍,这样就不符合 不写重复代码 的原则,很明显 教师 也是 ,拥有 的一切属性和能力,同时也具备普通人不具备的属性和能力,因此我们可以在 的定义之上来定义 教师 ,这就是类的继承,继承的类叫做 子类 ,被继承的类叫 父类子类 拥有 父类 的一切特性并可以定义自己特有的特性,在 ClassLesson 文件夹下新建一个 Teacher.swift 文件

class Teacher: Person {
    
}

类的继承方式如上,冒号 + 父类型,我们定义了一个新的 Teacher 类,下面就来为其定义它独有的特性

class Teacher: Person {
    
    /** 教师证*/
    var workID: String = ""
    
    /** 所属学校*/
    var school: String = ""
    
    /** 所任学科*/
    var subject: String = ""
    
}
func lessonRun() {
    let person = Teacher.init(id: "1234567890", hometown: "YiChang")
    person.name = "张琦"
    person.age = 25
    person.gender = .female
    person.workID = "9876543210"
    person.school = "Huazhong University of Science and Technology"
    person.subject = "Sociology"
}

在 Person 类的定义中有一个自我介绍的函数,Teacher 类同样有此函数,但是老师的自我介绍可能需要新增一些内容,我们可以在 Teacher 类中重写自我介绍的方法

class Teacher: Person {
    
    /** 教师证*/
    var workID: String = ""
    
    /** 所属学校*/
    var school: String = ""
    
    /** 所任学科*/
    var subject: String = ""
    
    override func makeIntroduction() {
        //如果需要执行父类中的函数定义,使用 super 表示父类
        super.makeIntroduction()
        print("我是一名 \(subject) 教师")
        print("我在 \(school) 任教")
    }
    
}
func lessonRun() {
    let person = Teacher.init(id: "1234567890", hometown: "YiChang")
    person.name = "张琦"
    person.age = 25
    person.gender = .female
    person.workID = "9876543210"
    person.school = "Huazhong University of Science and Technology"
    person.subject = "Sociology"
    person.makeIntroduction()
}

存储属性不能重写,计算属性和函数可以重写,重写时如果需要执行父类对应的函数或者计算属性,使用 super 来指向父类

静态属性和静态函数

以上的属性和函数都属于某个真实存在的实例,而有些属性或者函数则需要设计成 类型 共享,假如我们要给 Person 这个类新增一个 生物学定义 的属性,显然 生物学定义 这样的属性是属于 Person 这个类型,而非具体的某个 Person 的实例,就需要用 static 将其声明为静态属性

class Person {
    
    static let biologyDefinition = "人属于真核域,动物界,后生动物亚界,后口动物总门,脊索动物门,脊椎动物亚门,羊膜总纲,哺乳纲,兽亚纲,真兽次亚纲,灵长目,真灵长半目,直鼻猴亚目,人猿次目,狭鼻下目,真狭鼻小目,人猿超科,人科,人亚科,人族,人属,人亚属,智人种"
    
    let id: String
    var name: String = ""
    let hometown: String
    var age: Int = 0
    var gender: Gender = .unknown

}
func lessonRun() {
    print(Person.biologyDefinition)
    print(Teacher.biologyDefinition)
}

在使用静态属性的时候并不需要创建一个实例,因为静态属性属于类型本身,不属于某个实例,把 生物学属性 改成函数

class Person {
    
    static func biologyDefinition() -> String {
        return "人属于真核域,动物界,后生动物亚界,后口动物总门,脊索动物门,脊椎动物亚门,羊膜总纲,哺乳纲,兽亚纲,真兽次亚纲,灵长目,真灵长半目,直鼻猴亚目,人猿次目,狭鼻下目,真狭鼻小目,人猿超科,人科,人亚科,人族,人属,人亚属,智人种"
    }
    
    let id: String
    var name: String = ""
    let hometown: String
    var age: Int = 0
    var gender: Gender = .unknown

}
func lessonRun() {
    print(Person.biologyDefinition())
    print(Teacher.biologyDefinition())
}

静态函数还可以用 class 来修饰,用 class 修饰的静态函数唯一的区别就是可以在子类中重写

class Person {
    
    class func biologyDefinition() -> String {
        return "人属于真核域,动物界,后生动物亚界,后口动物总门,脊索动物门,脊椎动物亚门,羊膜总纲,哺乳纲,兽亚纲,真兽次亚纲,灵长目,真灵长半目,直鼻猴亚目,人猿次目,狭鼻下目,真狭鼻小目,人猿超科,人科,人亚科,人族,人属,人亚属,智人种"
    }
    
    let id: String
    var name: String = ""
    let hometown: String
    var age: Int = 0
    var gender: Gender = .unknown

}
class Teacher: Person {
    
    override class func biologyDefinition() -> String {
        return "人,教师"
    }
    
    /** 教师证*/
    var workID: String = ""
    
    /** 所属学校*/
    var school: String = ""
    
    /** 所任学科*/
    var subject: String = ""
    
}
func lessonRun() {
    print(Person.biologyDefinition())
    print(Teacher.biologyDefinition())
}

嵌套类型

我们可以把 Gender 枚举的定义写在 Person 类的定义中

class Person {
    
    enum Gender {
        case unknown
        case male
        case female
    }
    
}

对于 Person 类自身来说使用没有什么区别,但是在其他地方要使用这个枚举,就需要像下面这样

func lessonRun() {
    let person = Teacher.init(id: "1234567890", hometown: "YiChang")
    person.name = "张琦"
    person.age = 25
    person.gender = .female
    let male = Person.Gender.male
    let female = Person.Gender.female
}

类型扩展

我们可以在类型的定义之外为某个类型新增功能,包括函数和计算属性,叫做 类型扩展

extension Person {
    
    /** 伴侣*/
    var mate: Person? {
        return nil
    }

    /** 吃*/
    func eat() {
        
    }
    
    /** 喝*/
    func drink() {
        
    }
    
    /** 睡觉*/
    func sleep() {
        
    }
    
}

将这段代码写在 Person 类型定义之外,Person 类型现在拥有了吃,喝,睡的函数和 伴侣 的计算属性,我们可以通过扩展为任何类型新增功能,包括 Swift 基础类型

结构体

结构体 是相对于 更加轻量的封装,多用于封装简单的数据类型,例如 这样的类型

struct Point {
    
    var x: Double = 0
    var y: Double = 0
    
    init(x: Double, y: Double) {
        self.x = x
        self.y = y
    }
    
    //对称点
    var symmetrical: Point {
        return Point.init(x: -x, y: -y)
    }
    
    //判断与另一个点是否相等
    func isEqualTo(_ another: Point) -> Bool {
        return x == another.x && y == another.y
    }
    
    //对于结构体来说,如果要在函数中更改自身的属性,函数必须声明为 mutating ,表示这个函数只有变量可以使用
    mutating func reset() {
        x = 0
        y = 0
    }
    
}

let point1 = Point.init(x: 1, y: 2)
var point2 = Point.init(x: 3, y: 4)
point2.reset()

结构体使用起来跟类没有多大区别,实际上我们之前用过的所有基础数据类型,包括整数,字符串,浮点数都是结构体,结构体跟类最大的区别在于,一个是值类型,一个是引用类型

值类型和引用类型

func lessonRun() {
    let person = Person.init(id: "1234567890", hometown: "ChongQing")
    person.name = "JiangMing"
    person.age = 25
    person.gender = .male
    let anotherPerson = person
    anotherPerson.name = "JiangXiaoMing"
    print(person.name)
    
    let point = Point.init(x: 0, y: 0)
    var anotherPoint = point
    anotherPoint.x = 1
    anotherPoint.y = 1
    print(point)
}

差不多的两段代码,结果却大不相同,Person 是一个类,属于引用类型,当我们把 Person 的实例赋值给另外一个常量时,仅仅表示现在两个常量 person 和 anotherPerson 都指向了同一个实例,所以我们无论调用哪个常量没有任何区别

而 Point 是一个结构体,属于值类型,值类型以拷贝的形式进行赋值,也就是说当我们执行 var anotherPoint = point 这句话的时候,实际上是讲 point 当前的值原原本本地复制一份赋值给 anotherPoint ,之后就没有任何关联了,因此改变 anotherPoint 的值并不影响 point 的值

什么时候用 什么时候用 结构体 ,例子中已经给出了答案,在模拟现实问题的时候, 这样的东西是不可能以拷贝的形式存在, 是一个独立的个体,完成了一生的任务自然被销毁,随便一调用就拷贝出了一个一模一样的人那就全乱套了,但是像 地点 这样的东西只是一个信息载体,不具有独立存在的特点,我告诉你我的地点,只是将我当前的地点的信息拷贝一份给你,但是不能说我把我的地点告诉了你,你就能对我的地点这样的属性进行任何修改,除非你有超能力能让我飞

这节课我们初步学习了类与结构体的知识,已经可以开始对实际问题进行抽象模拟了,下一节课我们学习另一个重要知识:协议

上一篇:函数
当前篇:类与结构体
下一篇:协议

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

推荐阅读更多精彩内容

  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,732评论 2 9
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,581评论 18 399
  • 1.ios高性能编程 (1).内层 最小的内层平均值和峰值(2).耗电量 高效的算法和数据结构(3).初始化时...
    欧辰_OSR阅读 29,320评论 8 265
  • 在清明那一天,我们来到了外公外婆家,那是一个美丽的小村子,车疾驰过来,狗也汪汪的叫,好想在欢迎我们到来,...
    _丽丽_阅读 154评论 0 0