[Swift] The Swift Programming Language - 控制流/闭包/枚举/属性/方法/下标/继承

Control Flow

For in & For-Condition-Increment

主要有for in以及传统的for还有switch,例如for index in 1...5。这种知识循环内使用的对象例如这里的index,是不需要提前用let声明的,但是如果循环外你也想用就要先声明了哦~ 如果是想遍历string的字符还可以for character in "Hello"

如果你不需要用到index,甚至可以for _ in 1...5,这样其实就是循环了5次,然后你在block里面是拿不到这是第几次循环的。

对于dict之类的也可以用for in来执行:

let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
    print("\(animalName)s have \(legCount) legs")
}

如果是increment的那种遍历for (var index = 0; index < 3; ++index)在swift3以后已经不能用了,只能改成用for in + enumerated()了~ 还可以用for (index, value) in numberList.enumerated()

for animal in numberOfLegs.enumerated() {
    print("the \(animal.offset)th animal is \(animal)")
}

输出:
the 0th animal is (offset: 0, element: (key: "spider", value: 8))
the 1th animal is (offset: 1, element: (key: "ant", value: 6))
the 2th animal is (offset: 2, element: (key: "cat", value: 4))

如果你想反向遍历还可以酱紫numberList.enumerated().reversed()


Switch

和正常的switch的区别是,这里不用break,默认就是每个case会break,如果你需要不break直接添加fall through即可。另外的区别就是swift里面的switch不仅仅可以针对int,还可以针对string、character之类的~

而且你还可以把多个case合成一个例如case acase b合成为case a, b

并且case还可以提供range,例如一个int是99,你可以case 1...100

还可以用_来匹配一个任意字符:

let somePoint = (1, 1)
switch somePoint {
case (0, 0):
    print("(0, 0) is at the origin")
case (_, 0):
    print("(\(somePoint.0), 0) is on the x-axis")
case (0, _):
    print("(0, \(somePoint.1)) is on the y-axis")
case (-2...2, -2...2):
    print("(\(somePoint.0), \(somePoint.1)) is inside the box")
default:
    print("(\(somePoint.0), \(somePoint.1)) is outside of the box")
}

还可以用临时常量来在case里面使用:case (let x, 0): print("on the x-axis with an x value of \(x)"),这里用let因为其实后面是不会改的~

case是不能有空描述的,所以如果想忽略case可以通过break

  • where

switch还可以和where结合来check一些以前要用 if 来判断的事儿:

let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
    print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
    print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
    print("(\(x), \(y)) is just some arbitrary point")
}
  • fallthrough

会在满足了某个case以后继续判断下一个case。但不保证一定会到最后哦,如果中间某个满足了并且没有fallthrough就会跳出啦

let sum = 10
switch sum {
case 1,2,10:
    print("A")
    fallthrough
case 3,2,10:
    print("B")
case 4,2,10:
    print("C")
default:
    print("D")
}

输出:
A
B
  • label

swift还支持类似goto那种的label,你可以给代码段加上label,然后break到那个label或者continue到那个label的地方。


Functions

func sayHello(personName: String) -> String {
    let greeting = "Hello, " + personName + "!"
    return greeting
}

print(sayHello(personName: "Anna"))

没有return type的func其实也有return,只是类型是Void,也就是一个空的元组,还可以用()来替代。

可以返回多个数值例如:func count(string: String) -> (vowels: Int, consonants: Int, others: Int)注意一定要返回一个有名字的元组

func还可以区分内外名字~ 例如你有个内部变量叫sum,然后你传入的参数还是sum,如果觉得重复,可以让这个参数的外部名为sum,内部名为s,例如酱紫func join(string s1: String, toString s2: String, withJoiner joiner: String)

func someFunction(externalParameterName localParameterName: Int) { // function body goes here, and can use localParameterName
// to refer to the argument value for that parameter
}

你还可以给参数加默认值:func someFunction(externalParameterName localParameterName: Int = 0),这样就可以不给出参数了~ 例如someFunction()这么调用~

swift也支持可变参数传入...,也就是你可以传入0或者n个同类型的参数,内部可以用数组的形式使用,但每个func最多有一个这样的参数,要不就混啦,以及必须放到最后

func arithmeticMean(numbers: Double...) -> Double {
    var total: Double = 0
    for number in numbers {
        total += number
    }
    
    return total / Double(numbers.count)
}

默认的传入参数的类型都是let的,也就是不可变的,在函数范围内你只能使用外边传入的参数但是不能改变它,如果你想改变它的话,需要将它声明为var,这样的话其实是给了你一个原参数的可变copy~
func alignRight(var string: String, count: Int, pad: Character) -> String

因为如果用var修改其实并没有改到原来的值,所以如果你想修改传入的参数,需要加inout注意哦,`inout`如果用的话就不能使用`var`或者`let`来修饰了哦func swapTwoInts(inout a: Int, inout b: Int)

调用的时候需要用&来修饰传入的参数哦:

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)

函数类型就是他的输入&输出,例如(Int, Int) -> Int,就是输入两个int输出一个int~ 如果要是没有输入参数也没有输出就是() -> (),在swift里面可以简化为()

通过函数类型就可以定义函数了:var mathFunction: (Int, Int) -> Int = addTwoInts

你可以用函数类型来定义参数,其实函数类型和其他类型例如Int之类的完全是一样的:

func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int) { println("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5) // prints "Result: 8"

如果return type是func会看起来很神奇:chooseStepFunction就是return了一个函数~

func stepForward(input: Int) -> Int {
    return input + 1
}
func stepBackward(input: Int) -> Int {
    return input - 1
}
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
    return backwards ? stepBackward : stepForward
}

你不仅可以定义全局的func,还可以在函数体内定义nested functions,类似酱紫:

func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
    func stepForward(input: Int) -> Int { return input + 1 }
    func stepBackward(input: Int) -> Int { return input - 1 }
    return backwards ? stepBackward : stepForward
}

Closures

  • Global functions are closures that have a name and do not capture any values. 正常的函数不捕捉(增加引用)
  • Nested functions are closures that have a name and can capture values from their enclosing function. 函数内的会捕捉传入自己的参数
  • Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context. 闭包也会捕捉自己区域内的参数

闭包是酱紫的,闭包中也可以用let / var/ inout的:

{ ( parameters ) -> return type in statements
}

举个逆序排序的例子:

reversed = sort(names, { (s1: String, s2: String) -> Bool in 
  return s1 > s2
})

in代表着闭包的声明已经结束了,下面就是block的实现了。

当类型是可以infer的时候,例如参数的type就是(string, string) -> Bool,那么传入的内容就可以不用标明类型了,因为一定是和期待的类型一致的例如:reversed = sort(names, { s1, s2 in return s1 > s2 } )

如果block的body只有return xxx,甚至可以省略return改成酱紫reversed = sort(names, { s1, s2 in s1 > s2 } )

swift的$0, $1, $2可以用于指代第一、二、三个参数。所以上面的可以再简化为reversed = sort(names, { $0 > $1 } )这种情况下参数会和sort的定义去核对参数的类型,然后放到0 or1里面,这样就可以省略参数列表了,这里 in 也是可以省略的,因为整个block都是由body组成。

因为string其实有大小比较的方法,所以上面的还可以再简化一下变为reversed = sort(names, >)酱紫。

如果closure恰好是尾调用,正好是参数的最后一个,并且这个block会比较长,那么可以调用的时候把closure放到外边~

func someFunctionThatTakesAClosure(closure: () -> ()) {
    // function body goes here
}

// here's how you call this function without using a trailing closure:
someFunctionThatTakesAClosure (closure: {
    // closure's body goes here
})

someFunctionThatTakesAClosure() {
    // how you call this function with a trailing closure instead:
}

如果是这样简化一下,上面的sort还以改为酱紫:reversed = sort(names) { $0 > $1 }

当只有一个参数并且是closure的时候,调用甚至可以忽略函数的()这个主要是在array的map方法之类的时候可以使用

let strings = numbers.map {
    (var number) -> String in
    var output = ""
    while number > 0 {
        output = digitNames[number % 10]! + output
        number /= 10 }
    return output
}

这里number不需要指定type是因为它作为参数是可以infer出来的,然后声明为var是因为需要在之后的block里面修改。(swift 4以后好像不能给入参加var修饰以后修改了,强制是let的,但inout可以

值的捕获

先举个nest func的例子:

func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}

incrementor 其实是使用了外部的 runningTotal 以及 amount,他自己内部没有定义。这个时候其实是捕捉了外部的变量的~

func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }
    runningTotal = 10
    return incrementor
}

var i1 = 0
i1 = makeIncrementor(forIncrement: 10)() //20
i1 = makeIncrementor(forIncrement: 5)() //15
i1 = makeIncrementor(forIncrement: 6)() //16
i1 = makeIncrementor(forIncrement: 7)() //17

可以看到closure的捕捉不是字面量的copy,因为在它之后修改runningTotal的值也生效了,所以我猜测这里还是指针的捕捉。并且如果你在closure里面改了runningTotal并立刻调用这个closure,会发现外部的数值变化了,这里是很类似__block的捕捉的。也就是这closure是捕捉了外部变量的引用的,确保不会被释放(所以这里需要注意循环引用哦)

注意这里不是static,所以每次closure执行的时候runningTotal都是新的值,另外swift 3已经不支持局部static了,如果想让属性static,需要把static修饰类里面的变量,不能是函数里面的局部变量

closure是引用类型,多个变量如果指向同一个closure,实际上只是使用了同一个引用


枚举

定义类似酱紫:

enum CompassPoint {
    case North
    case South
    case East
    case West
}

还可以一行写~
enum Planet {
  case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}

如果初始化变量的时候已经是某个枚举了,之后就可以只提供值不写类型,因为这个变量的类型可以infer出来,例如这样初始化var directionToHead = CompassPoint.West,然后改的话只要酱紫就好directionToHead = .East

switch的时候也是,只要简写值就可以啦~

枚举还可以用于存储关联对象哦~

enum Barcode {
  case UPCA(Int, Int, Int) 
  case QRCode(String)
}

定义类型Barcode的对象,并且可以关联规定的元组:var productBarcode = Barcode.UPCA(8, 85909_51226, 3)或者productBarcode = .QRCode("ABCDEFGHIJKLMNOP")

在switch的时候可以把关联的元组提取出来:

switch productBarcode {
    case .UPCA(let numberSystem, let identifier, let check):
        print("UPC-A with value of \(numberSystem), \(identifier), \(check).")
    case .QRCode(let productCode):
        print("QR code with value of \(productCode).")
}

如果枚举需要默认值raw value可以酱紫:

enum ASCIIControlCharacter: Character {
    case Tab = "\t"
    case LineFeed = "\n"
    case CarriageReturn = "\r"
}

如果想从枚举值变为raw value可以用Planet.Earth.toRaw(),如果想反向从value变枚举值可以Planet.fromRaw(7)这样得到的就是枚举类型的数据啦


类和结构体

Classes and structures in Swift have many things in common. Both can:

  • Define properties to store values
  • Define methods to provide functionality
  • Define subscripts to provide access to their values using subscript syntax
  • Define initializers to set up their initial state
  • Be extended to expand their functionality beyond a default implementation
  • Conform to protocols to provide standard functionality of a certain kind

Classes have additional capabilities that structures do not:

  • Inheritance enables one class to inherit the characteristics of another.

也就是类和结构体很多方面都是一致的,除了class有继承的能力

Structures are always copied when they are passed around in your code, and do not use reference counting. struct传递的时候都是值引用哦

定义:(注意类名大驼峰,属性名小驼峰)

class SomeClass {
// class definition goes here
}
struct SomeStructure {
// structure definition goes here
}

swift允许你直接set给你的属性的属性一个value,不用创建一个新的对象然后赋值属性:someVideoMode.resolution.width = 1280

struct是有一个默认的初始化函数的,只要指定参数名即可,例如let vga = Resolution(width: 640, height: 480),但是class是没有默认的初始化函数的哦

struct和枚举都是值类型,也就是在传递的时候是通过copy传递的,不是传递引用。This means that any structure and enumeration instances you create—and any value types they have as properties—are always copied when they are passed around in your code. 但是class传递是引用传递哦

判断class的对象相等的时候,用===是严格相等,就是指向同一个对象地址,但是==可能是即使对象不是同一个,但各个参数一致就算同一个对象。

所以其实考虑用struct还是class的时候,可以考虑一下传递的时候希望是值引用还是指针引用~ 所以其实一般用的还是class

NSArray 和 NSDictionary 传递的时候是通过refer而不是值哦~ 但是array以及dictionary不是酱紫的,是传递的copy,所以swift里面的数组和词典和OC的还是有些许不一样的

如果数组里面存的是值类型,在array赋值的时候会把自己重新copy一下付给新的变量,如果里面存的是引用类型,则会复制指针:

var ages = ["Peter": 23, "Wei": 35, "Anish": 65, "Katya": 19]
var copiedAges = ages
copiedAges["Peter"] = 24
print(ages["Peter"])

array比较神奇,copy只会在你可能会append / delete / insert / replace的时候再去copy,有点类似线程fork,于是在没有操作的时候,其实指向的还是一个array。

var a = [1, 2, 3]
var b = a
var c = a
a[0] = 42
print(a[0]) // 42
print(b[0]) // 42
print(c[0]) // 42

但是如果你append一个对象就不一样了哦:

a.append(4)
a[0] = 777
print(a[0]) // 777
print(b[0]) // 42
print(c[0]) // 42

==!=可以用于判断两个队列是不是具有相同的member,而非指向array的指针一致。


属性

对于存储属性而言,如果是struct的属性,并且把struct对象赋值给一个let对象,那么这个对象的属性是不能被修改的,即使属性是var声明的:

struct FixedLengthRange {
    var firstValue: Int
    let length: Int
}

let rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
rangeOfThreeItems.firstValue = 6
// 将会报错Cannot assign to property: 'rangeOfThreeItems' is a 'let' constant

This behavior is due to structures being value types. When an instance of a value type is marked as a constant, so are all of its properties. 当值类型被标记为constant,它的所有属性也是constant。然而,对于class不是这样的哦。

@lazy表明的属性就是懒加载的,注意需要表明为var不能是let哦,因为只有在第一次被使用的时候这个属性才会init,也就是最开始是没有值的~例如lazy var importer = DataImporter()

懒加载比较适用于初始化比较耗费资源的(比如init的时候需要做I/O文件操作的),以及可能初始化的值会和其他有关系的,需要等这个对象init完再有的属性~~

注意swift是木有实例变量的概念的,也就是属性和实例变量都是一个,不会有_属性名的对象啦,也是防止不知道操作哪个吧,虽然OC转过来不太习惯没有实例变量

计算属性是会计算一个value,而不是存储一个值。they provide a getter and an optional setter to retrieve and set other properties and values indirectly

struct Point {
    var x = 0.0
    var y = 0.0
}
struct Size {
    var width = 0.0
    var height = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    var center : Point {
        get {
            print("\(#function) getter is invoked!")
            let centerX = origin.x + size.width / 2.0
            let centerY = origin.y + size.height / 2.0
            return Point(x: centerX, y: centerY)
        }
        set(newcenter) {
            print("\(#function) setter is invoked!")
            origin.x = newcenter.x - size.width / 2.0
            origin.y = newcenter.y - size.height / 2.0
        }
    }
}

var squre = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0))
let initcenter = squre.center
squre.center = Point(x: 15.0, y: 15.0)

这里rect的center就是计算属性~ 是由origin和size计算出来的~ 提供getter以及setter。默认setter的参数是newValue,如果不需要重命名可以不写(newcenter)的,直接使用newValue即可。

如果只有get的计算属性就是read-only的~ 如果只有get的情况下其实是可以省略get以及花括号的,变为酱紫:

struct Rect {
    var origin = Point()
    var size = Size()
    var center : Point {
        print("\(#function) getter is invoked!")
        let centerX = origin.x + size.width / 2.0
        let centerY = origin.y + size.height / 2.0
        return Point(x: centerX, y: centerY)
    }
}

属性值观察器willSet(默认参数是newValue)以及didSet(默认参数是oldValue)其实就类似KVO~ 会在属性set之前以及之后调用~

如果这个属性的setter你有,直接在setter里面做你想在set之前以及之后做的事情即可,注意init的时候赋值不会触发willset didset哦

var totalSteps: Int = 0 {
    willSet(newTotalSteps) {
        print("About to set totalSteps to \(newTotalSteps)")
    }
    didSet {
        if totalSteps > oldValue {
            print("Added \(totalSteps - oldValue) steps")
        }
    }
}

如果在didSet里面改变属性值是不会再次触发willSet和didSet的,但也可以成功改值~

全局变量or常量默认是lazy的,所以不用特殊标记@lazy,但是局部变量不是~

类属性是不属于instance的,而且必须设置default值,因为他和属于对象的property不一样,因为类其实本身没有init方法,不能像对象一样在init的时候赋值。

struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        // return an Int value here
        return 10;
    }
}
enum SomeEnumeration {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        // return an Int value here
        return 10;
    }
}

class SomeClass {
    static var computedTypeProperty: Int {
        // return an Int value here
        return 10;
    }
}

使用的时候可以println(SomeClass.computedT ypeProperty)酱紫即可。


Methods

在类里面使用property以及方法的时候,会默认认为是self.的,所以如果你不加self也是可以的。唯一冲突的时候就是方法参数名和property名称重复,这个时候方法参数名是优先的,如果想refer to property需要加self的哦

Structures and enumerations值类型的属性是不能改的,如果要必须在方法前加mutating,在方法里面甚至改掉self都可以。

struct Point2 {
    var x = 0.0, y = 0.0
    mutating func moveByX(deltaX: Double, y deltaY: Double) {
        x += deltaX
        y += deltaY
    }
}

// self修改
enum TriStateSwitch {
    case Off, Low, High
    mutating func next() {
        switch self {
        case .Off:
            self = .Low
        case .Low:
            self = .High
        case .High:
            self = .Off
        }
    }
}

但是如果用let修饰一个struct,仍旧是不可以调用这个struct的mutating的方法的:

let fixedPoint = Point(x: 3.0, y: 3.0) 
fixedPoint.moveByX(2.0, y: 3.0)
// this will report an error
  • 类方法对于class而言你可以通过在func之前声明class来定义,对于枚举和struct,可以用static来声明类方法。在OC里面只可以给class定义类方法,swift里面就很随意都可以~
class SomeClass {
    class func someTypeMethod() {
        // type method implementation goes here
    }
}

类方法里面的self指的是类,而非对象~ 在类方法里面可以引用类属性,但是不可以引用对象属性哦。


Subscripts

通过下标取和设置值是可以定义的:

subscript(index: Int) -> Int {
    get {
        // return an appropriate subscript value here
    }
    set(newValue) {
        // perform a suitable setting action here
    }
}

# 只读
subscript(index: Int) -> Int {
    // return an appropriate subscript value here
}

举个例子:

struct TimesTable {
    let multiplier: Int
    subscript(index: Int) -> Int {
        return multiplier * index
    }
}
let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])")

dictionary的下标返回值是optional的,例如int?,因为不是所有key都有value的,可能这个key不存在啦,而且可以通过将value设为nil来删除这个key-value对

还可以定义多个参数的下标哦,类似二维数据就可以通过多个参数取值:subscript(row: Int, column: Int) -> Double,然后就可以这样取值啦matrix[0, 1]


Inheritance

swift木有一个类似OC里面的NSObject的通用基类,所以如果不是继承了别的类,可以不用声明继承~

继承的声明时酱紫的class SomeClass: SomeSuperclassUnlike Objective-C, initializers are not inherited by default in Swift. swift里面的init是必须的,不会默认继承父类的

插一句swift里面的description是酱紫的:

class SomeClass {
    var description:String {
        let keys =   ["access_token","uid","remind_in","screen_name","avatar_large"]
        return keys.description
    }
}

print(SomeClass().description)

如果是覆写父类的方法属性必须有override关键字,编译时就会去check是不是覆写了父类的方法,如果父类没有这个方法会报错的。

属性继承的时候如果同时提供了getter和setter就是读写属性,如果只提供getter就是只读属性,继承的时候可以把父类的只读改为读写属性,但是不能把读写属性限制为只读哦

If you provide a setter as part of a property override, you must also provide a getter for that override. If you don’t want to modify the inherited property’s value within the overriding getter, you can simply pass through the inherited value by returning super.someProperty from the getter

如果属性是let或者read-only,那么就不能增加observer,因为根本不会有set的过程。并且如果覆写了setter也可以不用增加observer,因为在setter里面可以写~

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