3、GCD-swift

GCD 一直以来是基于 c 语言的。
apple 为了使 GCD 使用更加的 swift 化。 对 GCD 进行了进行了改造。
以下所有代码都是基于 Swift3.0


先来段废话:
(不了解基本概念的建议看看, 了解的直接略过!)

GCD

Grand Central Dispatch (GCD)是Apple开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并行任务。在Mac OS X 10.6雪豹中首次推出,也可在IOS 4及以上版本使用。

GCD 中两个重要重要概念 —— 队列 & 任务

队列

队列是一种特殊的线性表,采用FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。

队列的主要作用是用来存放任务。

GCD会自动将队列中的任务取出,放到对应的线程中执行。

**队列分类: **

  • 串行队列(Serial Dispatch Queue): 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务

  • 并发队列(Concurrent Dispatch Queue): 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务), 并发功能只有在异步(dispatch_async)函数下才有效
    > 由于队列同步执行不具有开启线程的能力,异步执行才可以开启线程。
    > 并发队列在异步执行才有效。

队列执行任务的方式:

  • 同步:在当前线程中执行,当前代码不执行完,就不能够执行下一条代码。会阻塞当前线程。

  • 异步:在另一条线程中执行(不用等待当前代码执行完,就能够执行下一条),不会阻塞当前线程。

常见队列:

为了方便 GCD 使用,苹果默认提供了主队列和全局队列。 我们不需要创建只需要获取。

  • 主队列 (串行)
let mainQueue = DispatchQueue.main
  • 全局队列 (并发)
let globalQueue = DispatchQueue.global()

全局队列默认是并发队列,在不进行第三方框架或者大型的商业应用开发,全局队列基本够用。

任务

需要执行操作, 任务是使用闭包( oc: block) 封装的代码块。

废话到此完毕!


1. 项目开发中 GCD 代码使用

print("DispatchQueue.main.sync: befor", Thread.current)
DispatchQueue.global().async {
       print("DispatchQueue.global().async: Time task", Thread.current, "\n --: 耗时操作在后台线程中执行!")
            
      DispatchQueue.main.async {
          print("DispatchQueue.main.async: update UI", Thread.current, "\n --: 耗时操作执行完毕后在主线程更新 UI 界面!")
      }
}
print("DispatchQueue.main.sync: after", Thread.current)


// 打印:
DispatchQueue.main.sync: befor <NSThread: 0x60800007abc0>{number = 1, name = main}
DispatchQueue.main.sync: after <NSThread: 0x60800007abc0>{number = 1, name = main}
DispatchQueue.global().async: Time task <NSThread: 0x6080002662c0>{number = 3, name = (null)} 
 --: 耗时操作在后台线程中执行!
DispatchQueue.main.async: update UI <NSThread: 0x60800007abc0>{number = 1, name = main} 
 --: 耗时操作执行完毕后在主线程更新 UI 界面!

/*
 after 执行的先后顺序是不确定的。是由 GCD 决定。
这里牵扯到一个概念: 线程间通讯! 说白的就是在线程间进行数据(信号)的传递。
*/

如果你只是单纯的找 GCD 在 swift 中怎么使用,看到这里你基本可以关掉这篇博客,开心的写代码去了。


2. GCD 使用 —— 精讲

先来了解 GCD 在 Swift 中的变化
GCD 框架头文件改变

import Dispatch.base         // 没有实质性内容
import Dispatch.block        // 没有实质性内容
import Dispatch.data         // 没有实质性内容
import Dispatch.group
import Dispatch.io
import Dispatch.object
import Dispatch.once        // 没有实质性内容
import Dispatch.queue
import Dispatch.semaphore
import Dispatch.source
import Dispatch.time
import Dispatch
import os_object

// 在 swift3.0 中新加的, OS_object 继承自 NSObject
// 在 GCD 中所有的对象都间接的继承自 NSObject。
import os_object
open class OS_object : NSObject {
}

Swift 中最大的变化其实就是更加的面相对象了,使用更加的方便简洁,摆脱了 OC 时代函数式的使用方式。

GCD 使用先来个个人小的总结:

口诀:同步不开异步开,串行开1条,并行开多条。

来,跟着哥一起念。( 我怕你们打我,你们还是别念了,我解释一下。)

同步不开异步开,串行开1条,并行开多条。
单纯这么简单的看是有误解的, 在我的评论中就可以看到。

具体的意思是:
队列中的任务同步执行,队列就不具有开启线程的能力, 队列中的任务异步执行,队列就具有开启线程的能力。
(同步和异步执行决定的是否有开启线程的能力)

如果队列具 **有开启线程的能力 (队列任务异步执行) ** 且队列是 串行队列 ,那么将会 开启 1 条线程
如果队列具 **有开启线程的能力 (队列任务异步执行) ** 且队列是 并发队列 ,那么将会 开启 多 条线程 。开启线程的条数由 GCD 决定。
** ( 串行和并发决定的是开几条线程 ) **

** 如果真正理解了上面这些,多 GCD 的使用和面试基本没什么障碍。 **

2.1 全局队列

全局队列是获取的,不是程序员创建的。
为了方便 GCD 的使用,apple 默认为我们提供的。
全局队列默认是并发队列,在不是进行第三方框架或者大型的商业应用开发,全局队列基本够用。

全局 ( 并发 ) 队列异步执行 :

并发队列异步(不阻塞当前线程)执行(队列就具有开启线程的能力), 队列会开启多条线程。

任务异步执行不会阻塞当前线程, 
   befor 在最前, 
   after 在任意位置,
   task 执行顺序不确定 —— 并发执行(index可以确认)。
   task 并发执行 —— 并发执行(number可以确认)。

异步开线程 number 可以确定开启了多条线程
   开的线程数由 GCD 决定。 可以看到线程的 number 有重复,是 GCD 对线程进行了复用。
func async() {
    
    print("DispatchQueue.global().async: befor", Thread.current)
    // 全局队列进行 10次异步
    for index in 0..<10 {

        DispatchQueue.global().async {
            print("DispatchQueue.global().async: task:(taskIndex:\(index)", Thread.current)
        }
    }
    print("DispatchQueue.global().async: after", Thread.current)
}

打印:
     DispatchQueue.global().async: befor <NSThread: 0x60800006a8c0>{number = 1, name = main}
     DispatchQueue.global().async: task:(taskIndex:1 <NSThread: 0x600000079780>{number = 3, name = (null)}
     DispatchQueue.global().async: task:(taskIndex:2 <NSThread: 0x6000000797c0>{number = 4, name = (null)}
     DispatchQueue.global().async: task:(taskIndex:0 <NSThread: 0x600000079880>{number = 5, name = (null)}
     DispatchQueue.global().async: task:(taskIndex:3 <NSThread: 0x608000074e00>{number = 6, name = (null)}
     DispatchQueue.global().async: task:(taskIndex:4 <NSThread: 0x600000079780>{number = 3, name = (null)}
     DispatchQueue.global().async: task:(taskIndex:5 <NSThread: 0x6000000797c0>{number = 4, name = (null)}
     DispatchQueue.global().async: after <NSThread: 0x60800006a8c0>{number = 1, name = main}
     DispatchQueue.global().async: task:(taskIndex:6 <NSThread: 0x600000079880>{number = 5, name = (null)}
     DispatchQueue.global().async: task:(taskIndex:7 <NSThread: 0x608000074e00>{number = 6, name = (null)}
     DispatchQueue.global().async: task:(taskIndex:8 <NSThread: 0x600000079780>{number = 3, name = (null)}
     DispatchQueue.global().async: task:(taskIndex:9 <NSThread: 0x6000000797c0>{number = 4, name = (null)}

全局 ( 并发 ) 队列同步执行 :

并发队列同步(阻塞当前线程)执行(队列就不具有开启线程的能力), 队列不会开启线程(代码都在主线程中执行)。

任务同步执行会阻塞当前线程, 
     befor 在最前, 
     after 在最后,
     task 执行顺序确定 —— 阻塞。
同步没有开启线程 number 可以确定没有开启多条线程。所有的代码都在 主线程中执行。
func sync() {
    print("DispatchQueue.global().sync: befor", Thread.current)
    for index in 0..<10 {
        DispatchQueue.global().sync {
            print("DispatchQueue.global().sync: task:(taskIndex:\(index))", Thread.current)
        }
    }
    print("DispatchQueue.global().sync: after", Thread.current)
}


打印: 
DispatchQueue.global().sync: befor <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:0) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:1) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:2) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:3) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:4) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:5) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:6) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:7) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:8) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: task:(taskIndex:9) <NSThread: 0x60800007fa80>{number = 1, name = main}
DispatchQueue.global().sync: after <NSThread: 0x60800007fa80>{number = 1, name = main}

2.2 主队列

主队列是获取的,不是程序员创建的,apple 默认为我们提供的。
(app 开发中,所有的 UI 更新操作都应该在主线程中进行)

主队列(串行)异步执行

主队列异步(不会阻塞当前线程)执行(队列就具有开启线程的能力), 队列会开启线程(开启的线程就是主线程)。

> 有朋友问我,异步会开启线程, 主队列异步就不会开启线程。 
> 我当时还信以为真。认为自己错误,说特殊情况特殊处理。 其实说白了就是学艺不精。
> 由于主队列是我们获取的,不是我们创建的,在某种意识中会认为主线程不是在主队列中创建的。(认为一开始就存在的。)


> 任务异步执行不会阻塞当前线程, 
 befor 在最前, 
 after 在第二,
 task 执行顺序确定 —— 串行执行(index可以确认)。

同步没有开启线程 number 可以确定没有开启多条线程。所有的代码都在 主线程中执行。

主队列异步的操作主要用在更新 UI 操作中。 具体参考 项目开发中 GCD 代码使用。

func async() {
        
        print("DispatchQueue.main.async: befor", Thread.current)
        for index in 0..<10 {
            DispatchQueue.main.async {
                print("DispatchQueue.main.async: task:(taskIndex:\(index)", Thread.current)
            }
        }
        print("DispatchQueue.main.async: after", Thread.current)
}

打印: 
DispatchQueue.main.async: befor <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: after <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:0 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:1 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:2 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:3 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:4 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:5 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:6 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:7 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:8 <NSThread: 0x60800006ddc0>{number = 1, name = main}
DispatchQueue.main.async: task:(taskIndex:9 <NSThread: 0x60800006ddc0>{number = 1, name = main}

主队列(串行)同步执行

执行的效果就俩字 ** 死锁**

主线程同步,在 Swift 中,编译阶段就报错,在 oc 中是在运行的时候才能发现。体现的主要是界面的 “假死”。


Snip20170316_1.png

2.3 自定义队列

/*
label:   队列名称
qos: 服务质量
     // 后台优先级
     public static let background: DispatchQoS
    // 实用工具优先级别(耗时操作,可以使用这个)
    public static let utility: DispatchQoS
    // 默认优先级(一般不是给程序员实用的,用来重置队列用的)
    public static let `default`: DispatchQoS
    //  用户期望优先级(不要放太耗时的操作)
    public static let userInitiated: DispatchQoS
    // 用户交互(希望尽快完成,用户很希望得到结果。个人觉得这个和主线的的线程优先级是一样的)
    public static let userInteractive: DispatchQoS
    // 未指定
    public static let unspecified: DispatchQoS


attributes: 队列属性
        // 并发
        public static let concurrent: DispatchQueue.Attributes
        // 初始化不活跃
        public static let initiallyInactive: DispatchQueue.Attributes

autoreleaseFrequency: 自动释放频率
          public enum AutoreleaseFrequency {
              // 继承
              case inherit
              // 工作项
              case workItem
              // 从来没有,永远不
              case never
           }
  target: 目标队列

( 这些参数我也没搞明白)
*/
public convenience init(label: String, 
                          qos: DispatchQoS = default, 
                   attributes: DispatchQueue.Attributes = default, 
         autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = default,
                       target: DispatchQueue? = default)

在没有搞明白参数的时候,由于 swift 可以使用默认参数, 我们可以使用默认参数。

自定义(串行)队列异步执行

// 使用默认的构造函数创建了一个队列(并不知队列是串行还是并发)
// api 文档没有给够足够的信息。 后期的文档描述完整后可以直接查看文档。

func async() {
        
        print("DispatchQueue(label: \"laughing\").async: befor", Thread.current)
        let queue = DispatchQueue(label: "laughing")
        for index in 0..<10 {
            queue.async {
                print("DispatchQueue(label: \"laughing\").async: task:(taskIndex:\(index)", Thread.current)
            }
        }
        print("DispatchQueue(label: \"laughing\").async: after", Thread.current)
 }

      /*
         这么写你会有一种串行队列并发执行的错觉。
        for index in 0..<10 {
            DispatchQueue(label: "laughing").sync {
                print("DispatchQueue(label: \"laughing\").sync: task:(taskIndex:\(index))", Thread.current)
            }
        }
       */

打印:
DispatchQueue(label: "laughing").async: befor <NSThread: 0x608000067180>{number = 1, name = main}
DispatchQueue(label: "laughing").async: after <NSThread: 0x608000067180>{number = 1, name = main}
DispatchQueue(label: "laughing").async: task:(taskIndex:0 <NSThread: 0x608000074200>{number = 3, name = (null)}
DispatchQueue(label: "laughing").async: task:(taskIndex:1 <NSThread: 0x608000074200>{number = 3, name = (null)}
DispatchQueue(label: "laughing").async: task:(taskIndex:2 <NSThread: 0x608000074200>{number = 3, name = (null)}
DispatchQueue(label: "laughing").async: task:(taskIndex:3 <NSThread: 0x608000074200>{number = 3, name = (null)}
DispatchQueue(label: "laughing").async: task:(taskIndex:4 <NSThread: 0x608000074200>{number = 3, name = (null)}
DispatchQueue(label: "laughing").async: task:(taskIndex:5 <NSThread: 0x608000074200>{number = 3, name = (null)}
DispatchQueue(label: "laughing").async: task:(taskIndex:6 <NSThread: 0x608000074200>{number = 3, name = (null)}
DispatchQueue(label: "laughing").async: task:(taskIndex:7 <NSThread: 0x608000074200>{number = 3, name = (null)}
DispatchQueue(label: "laughing").async: task:(taskIndex:8 <NSThread: 0x608000074200>{number = 3, name = (null)}
DispatchQueue(label: "laughing").async: task:(taskIndex:9 <NSThread: 0x608000074200>{number = 3, name = (null)}

** 由于只异步执行只开了1 条线程, 可只。 默认创建的队列是 串行队列**

自定义(串行)队列同步执行
串行队列同步执行,没有开启线程。 代码在主线程中执行。

  func sync() {
        print("DispatchQueue(label: \"laughing\").sync: befor", Thread.current)
        
        let queue = DispatchQueue(label: "laughing")
        for index in 0..<10 {
            queue.sync {
                print("DispatchQueue(label: \"laughing\").sync: task:(taskIndex:\(index))", Thread.current)
            }
        }
        
        /*
         这么写你会有一种串行队列并发执行的错觉。
        for index in 0..<10 {
            DispatchQueue(label: "laughing").sync {
                print("DispatchQueue(label: \"laughing\").sync: task:(taskIndex:\(index))", Thread.current)
            }
        }
        */
        
        print("DispatchQueue(label: \"laughing\").sync: after", Thread.current)
    }

打印: 
DispatchQueue(label: "laughing").sync: befor <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:0) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:1) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:2) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:3) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:4) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:5) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:6) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:7) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:8) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: task:(taskIndex:9) <NSThread: 0x600000071d40>{number = 1, name = main}
DispatchQueue(label: "laughing").sync: after <NSThread: 0x600000071d40>{number = 1, name = main}

自定义(并发)队列异步执行
(参考全局队列)

func async1() {
    
    print("DispatchQueue(label: \"laughing1\").async: befor", Thread.current)
    let queue = DispatchQueue(label: "laughing1",  attributes: DispatchQueue.Attributes.concurrent)
    for index in 0..<10 {
        queue.async {
            print("DispatchQueue(label: \"laughing1\").async: task:(taskIndex:\(index)", Thread.current)
        }
    }
    print("DispatchQueue(label: \"laughing1\").async: after", Thread.current)
}

打印
DispatchQueue(label: "laughing1").async: befor <NSThread: 0x600000261d80>{number = 1, name = main}
DispatchQueue(label: "laughing1").async: after <NSThread: 0x600000261d80>{number = 1, name = main}
DispatchQueue(label: "laughing1").async: task:(taskIndex:1 <NSThread: 0x60800026d380>{number = 3, name = (null)}
DispatchQueue(label: "laughing1").async: task:(taskIndex:3 <NSThread: 0x60800026d380>{number = 3, name = (null)}
DispatchQueue(label: "laughing1").async: task:(taskIndex:4 <NSThread: 0x60800026d380>{number = 3, name = (null)}
DispatchQueue(label: "laughing1").async: task:(taskIndex:2 <NSThread: 0x60800026d9c0>{number = 4, name = (null)}
DispatchQueue(label: "laughing1").async: task:(taskIndex:5 <NSThread: 0x60800026d380>{number = 3, name = (null)}
DispatchQueue(label: "laughing1").async: task:(taskIndex:7 <NSThread: 0x60800026d9c0>{number = 4, name = (null)}
DispatchQueue(label: "laughing1").async: task:(taskIndex:6 <NSThread: 0x60800026da00>{number = 5, name = (null)}
DispatchQueue(label: "laughing1").async: task:(taskIndex:0 <NSThread: 0x60800026db40>{number = 6, name = (null)}
DispatchQueue(label: "laughing1").async: task:(taskIndex:8 <NSThread: 0x60800026d380>{number = 3, name = (null)}
DispatchQueue(label: "laughing1").async: task:(taskIndex:9 <NSThread: 0x60800026d9c0>{number = 4, name = (null)}

自定义(并发)队列同步执行

func sync1() {
    print("DispatchQueue(label: \"laughing1\").sync: befor", Thread.current)
    
    
    let queue = DispatchQueue(label: "laughing1",  attributes: DispatchQueue.Attributes.concurrent)
    for index in 0..<10 {
        queue.sync {
            print("DispatchQueue(label: \"laughing1\").sync: task:(taskIndex:\(index))", Thread.current)
        }
    }
    
    /*
     这么写你会有一种串行队列并发执行的错觉。
     for index in 0..<10 {
     DispatchQueue(label: "laughing").sync {
     print("DispatchQueue(label: \"laughing\").sync: task:(taskIndex:\(index))", Thread.current)
     }
     }
     */
    
    print("DispatchQueue(label: \"laughing1\").sync: after", Thread.current)
}


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

推荐阅读更多精彩内容