可選值

問題及解決方法

哨崗值 - 可選值產(chǎn)生的背景

在編程世界中有一種非常通用的模式问芬,那就是某個操作是否要返回一個有效值。很多情況下贮配,一些操作由于各種原因沒有正常的返回期望的值囊卜,而是返回了一個“魔法”數(shù)來表示沒有返回真實(shí)的值。這樣的值被稱為“哨崗值”越妈。而由于我們忘記檢查哨崗值而導(dǎo)致程序出錯季俩,此外,這些哨崗值的檢查非常麻煩具有很多不確定性梅掠,可能我們需要查看文檔酌住,可能文檔也是錯誤的。

通過枚舉解決魔法數(shù)的問題

大多數(shù)語言支持某種類型的枚舉瓤檐,Swift更進(jìn)一步赂韵,他的枚舉中包含‘關(guān)聯(lián)值’的概念。也就是說枚舉可以在它們的值中包含另外的關(guān)聯(lián)的值挠蛉,如下:

enum Optional<T> {
  case None
  case Some(T)
}

獲取關(guān)聯(lián)值的唯一方法是使用 switch 或者 if case 語句祭示。和哨崗值不同,除非你顯式地檢查并解包谴古,你是不可能意外地使用到一個 Optional 中的值的质涛。

因此,Swift 中與 find 等效的方法 indexOf 所返回的不是一個索引值掰担,而是一個 Optional<Index>汇陆。它是通過協(xié)議擴(kuò)展實(shí)現(xiàn)的:

extension CollectionType where Generator.Element: Equatable {
  func indexOf(element: Generator.Element) -> Optional<Index> {
    for idx in self.indices where self[idx] == element {
      return .Some(idx)
    }
    // 沒有找到,返回 .None
    return .None
  }
}

可選值遵守 NilLiteralConvertible 協(xié)議带饱,因此你可以用 nil 來替代.None毡代;像上面 idx 這樣的非可選值將在需要的時候自動“升級”為可選值,這樣你就可以直接寫 return idx勺疼,而不用 return .Some(idx)教寂。

現(xiàn)在,用戶就不會錯誤地使用一個無效的值了:

  var array = ["one", "two", "three"]
  let idx = array.indexOf("four")
  // 編譯錯誤:removeIndex takes an Int, not an Optional<Int>
  array.removeAtIndex(idx)”

如果你得到的可選值不是 .None执庐,現(xiàn)在想要取出可選值中的實(shí)際的索引的話酪耕,你必須對其進(jìn)行“解包”:

switch array.indexOf("four") {
  case .Some(let idx):
    array.removeAtIndex(idx)
  case .None:
    break // 什么都不做
}

Swift 2.0 中引入了使用 ? 作為在 switch 中對 Some 進(jìn)行匹配的模式后綴的語法,另外轨淌,你還可以使用 nil 字面量來匹配 None:

switch array.indexOf("four") {
  case let idx?:
    array.removeAtIndex(idx)
  case nil:
    break // 什么都不做
}

可選值概覽

if let

使用 if let 來進(jìn)行可選綁定 (optional binding) 要比上面使用 switch 語句要稍好一些:

if let idx = array.indexOf("four") {
  array.removeAtIndex(idx)
}

if let idx = array.indexOf("four") where idx != array.startIndex {
  array.removeAtIndex(idx)
}

你也可以在同一個 if 語句中綁定多個值迂烁。更贊的是,后面的綁定值可以基于之前的成功解包的值來進(jìn)行操作递鹉。這在你想要多次調(diào)用一些返回可選值的函數(shù)時會特別有用盟步。

let urlString = "http://www.objc.io/logo.png" where url.pathExtension == "png"
if let url = NSURL(string: urlString),
data = NSData(contentsOfURL: url),
image = UIImage(data: data)
{
    let view = UIImageView(image: image)
    XCPlaygroundPage.currentPage.liveView = view
}

如果你需要在指定 if let 綁定之前執(zhí)行某個檢查的話,可以為 if 提供一個前置的條件躏结。如下:使用 NSScanner 來進(jìn)行掃描却盘,它將返回一個代表是否掃描到某個值的布爾值,在之后,你可以解包得到的結(jié)果谷炸。

let stringScanner = NSScanner(string: "myUserName123")
var username: NSString?
let alphas = NSCharacterSet.alphanumericCharacterSet()

if stringScanner.scanCharactersFromSet(alphas, intoString: &username),
let name = username
{
  print(name)
}

while let

while let 語句和 if let 非常相似北专,它代表一個當(dāng)遇到 nil 時終止的循環(huán)。

標(biāo)準(zhǔn)庫中的 readLine 函數(shù)從標(biāo)準(zhǔn)輸入中讀取一個可選字符串旬陡。當(dāng)?shù)竭_(dá)輸入末尾時拓颓,這個方法將返回 nil。

while let line = readLine() {
  print(line)
}

和 if let 一樣描孟,你可以在可選綁定后面添加一個 where 語句驶睦。如果你想在遇到 EOF 或者空行的時候終止循環(huán)的話,只需要加一個判斷空字符串的語句就行了匿醒。要注意场航,一旦條件為 false,循環(huán)就會停止 (也許你錯誤地認(rèn)為 where 條件會像 filter 那樣工作廉羔,其實(shí)不然)溉痢。

while let line = readLine() where !line.isEmpty {
  print(line)
}

let array = [1, 2, 3]
var generator = array.generate()
while let i = generator.next() {
  print(i)
}

雙重可選值

一個可選值本身也可以被使用另一個可選值包裝起來,這會導(dǎo)致可選值嵌套在可選值中憋他。假設(shè)你有一個字符串?dāng)?shù)組孩饼,其中的字符串是數(shù)字,你現(xiàn)在想將它們轉(zhuǎn)換為整數(shù)竹挡。最直觀的方式是用一個 map 來進(jìn)行轉(zhuǎn)換:

let stringNumbers = ["1", "2", "3", "foo"]
let maybeInts = stringNumbers.map { Int($0) }

你現(xiàn)在得到了一個元素類型為 Optional<Int> 的數(shù)組镀娶,也就是說,maybeInts 是 [Int?] 類型揪罕。
當(dāng)使用 for 循環(huán)遍歷這個結(jié)果數(shù)組時梯码,顯然每個元素都會是可選整數(shù)值,因?yàn)?maybeInts 含有的就是這樣的值:

for maybeInt in maybeInts {
  // maybeInt 是一個 Int? 值
  // 得到三個整數(shù)值和一個 `nil`
}

for...in 是 while 循環(huán)加上一個生成器的簡寫方式好啰,生成器的 next 函數(shù)返回的其實(shí)是一個 Optional<Optional<Int>> 值轩娶,或者說是一個 Int??。

當(dāng)循環(huán)到達(dá)最后一個值坎怪,也就是從 “foo” 轉(zhuǎn)換而來的 nil 時罢坝,從 next 返回的其實(shí)是一個非 nil 的值廓握,這個值是 .Some(nil)搅窿。while let 將這個值解包,并將解包結(jié)果 (也就是 nil) 綁定到 maybeInt 上隙券。解決方法:使用for case語法:

for case let i? in maybeInts {
    // i 將是 Int 值男应,而不是 Int?
    // 1, 2, 和 3
}
// 或者只對 nil 值進(jìn)行循環(huán)
for case nil in maybeInts {
    // 將對每個 nil 執(zhí)行一次
}

if var and while var

除了 let 以外,你還可以使用 var 來搭配 if 和 while:

if var i = Int(s) {
  i += 1
  print(i) // 打印 2
}

可選值是值類型娱仔,解包一個可選值做的事情是將它里面的值提取出來沐飘。所以使用 if var 這個變形和在函數(shù)參數(shù)上使用 var 類似,它只是獲取一個能在作用域內(nèi)使用的副本的簡寫,而并不會改變原來的值耐朴。

解包后可選值的作用域

有時候只能在 if 塊的內(nèi)部訪問被解包的變量確實(shí)讓人有點(diǎn)不爽借卧,但是這其實(shí)和其他一些做法并無不同。
不過如果你從函數(shù)中提早退出的話筛峭,情況就完全不同了铐刘。有時候你可能會這么寫:

func doStuff(withArray a: [Int]) {
  if a.isEmpty { return }
  // 現(xiàn)在可以安全地使用 a[0]
}

提早退出有助于避免惱人的 if 嵌套,你也不再需要在函數(shù)后面的部分再次重復(fù)地進(jìn)行判斷影晓。此外還可以利用swift的延時初始化來實(shí)現(xiàn):

func doStuffWithFileExtension(fileName: String) {
  let period: String.Index
  if let idx = fileName.characters.indexOf(".") {
    period = idx
  } else {
    return
  }

  let extensionRange = period.successor()..<fileName.endIndex
  let fileExtension = fileName[extensionRange]
  print(fileExtension)
}

雖然避免了if嵌套镰吵,但是這段代碼看起來很丑。我們在這里真正需要的其實(shí)是一個 if not let 語句挂签,其實(shí)這正是 guard let 所做的事情疤祭。

func doStuffWithFileExtension(fileName: String) {
    guard let period = fileName.characters.indexOf(".") else { return }

    let extensionRange = period.successor()..<fileName.endIndex
    let fileExtension = fileName[extensionRange]
    print(fileExtension)
}

在閱讀代碼時,guard 是一個明確的信號饵婆,它暗示我們“只在條件成立的情況下繼續(xù)”勺馆。最后 Swift 編譯器還會檢查你是否確實(shí)在 guard 塊中退出了當(dāng)前作用域,如果沒有的話侨核,你會得到一個編譯錯誤谓传。因?yàn)榭梢缘玫骄幾g器幫助,所以我們建議盡量選擇使用 guard芹关,即便 if 也可以正常工作续挟。

可選鏈

在 Objective-C 中,對 nil 發(fā)消息什么都不會發(fā)生侥衬。Swift 里诗祸,我們可以通過“可選鏈 (optional chaining)”來達(dá)到同樣的效果。

self.delegate?.callback()

如果你的可選值值中確實(shí)有值轴总,那么編譯器能夠保證方法肯定會被實(shí)際調(diào)用直颅。如果沒有值的話,這里的問號對代碼的讀者來說是一個清晰地信號怀樟,表示方法可能會不被調(diào)用功偿。

還有如下情況:

let dictOfArrays = ["nine": [0, 1, 2, 3, 4, 5, 6, 7]]
let sevenOfNine = dictOfArrays["nine"]?[7] ”

let dictOfFuncs: [String: (Int, Int) -> Int] = [
  "add": (+),
  "subtract": (-)
]
dictOfFuncs["add"]?(1, 1)

也可以使用可選值鏈來進(jìn)行賦值,如果它不是 nil 的話往堡,賦值操作將會成功:

splitViewController?.delegate = myDelegate”

nil 合并運(yùn)算符

很多時候械荷,你會想要解包一個可選值,如果可選值是 nil 時虑灰,就用一個默認(rèn)值來替代它吨瞎。你可以使用 nil 合并運(yùn)算符來完成這件事:

let stringteger = "1"
let i = Int(stringteger) ?? 0

當(dāng)你發(fā)現(xiàn)你在檢查某個語句來確保取值滿足條件的時候,往往意味著使用可選值會是一個更好的選擇穆咐。假設(shè)你要做的不是對空數(shù)組判定颤诀,而是要檢查一個索引值是否在數(shù)組邊界內(nèi):

let i = array.count > 5 ? a[5] : 0

不像 first 和 last字旭,通過索引值從數(shù)組中獲取元素不會返回Optional,不過我們可以對 Array 進(jìn)行擴(kuò)展來包含這個功能:

extension Array {
  subscript(safe idx: Int) -> Element? {
    return idx < endIndex ? self[idx] : nil
  }
}

現(xiàn)在你就可以這樣寫:

let i = array[safe: 5] ?? 0

合并操作也能夠進(jìn)行鏈接 — 如果你有多個可能的可選值崖叫,并且想要選擇第一個非 nil 的值遗淳,你可以將它們按順序合并:

let i: Int? = nil
let j: Int? = nil
let k: Int? = 42
let n = i ?? j ?? k ?? 0

可選值 map

在之前你看到過這個例子:

func doStuffWithFileExtension(fileName: String) {
    guard let period = fileName.characters.indexOf(".") else { return }
    let extensionRange = period.successor()..<fileName.endIndex
    let fileExtension = fileName[extensionRange]
    print(fileExtension)
}

我們可以稍作改變,現(xiàn)在不在 else 塊中從函數(shù)返回心傀,而是將 fileExtension 聲明為可選值洲脂,并且在 else 中將它設(shè)置為 nil:

func doStuffWithFileExtension(fileName: String) {
  let fileExtension: String?
  if let idx = fileName.characters.indexOf(".") {
  let extensionRange = idx.successor()..<fileName.endIndex
  fileExtension = fileName[extensionRange]
  } else {
  fileExtension = nil
  }
  print(fileExtension ?? "No extension")
}

Swift 中的可選值里專門有一個方法來處理這種情況,它叫做 map剧包。這個方法接受一個閉包恐锦,如果可選值有內(nèi)容,則調(diào)用這個閉包對其進(jìn)行轉(zhuǎn)換疆液。上面的函數(shù)用 map 可以重寫成:

func doStuffWithFileExtension(fileName: String) {
  let fileExtension: String? = fileName.characters.indexOf(".").map { idx in
    let extensionRange = idx.successor()..<fileName.endIndex
    return fileName[extensionRange]
  }
  print(fileExtension ?? "No extension")
}

顯然一铅,這個 map 和數(shù)組以及其他序列里的 map 方法非常類似。但是與序列中操作一系列值所不同的是堕油,可選值的 map 方法只會操作一個值潘飘,那就是該可選值中的那個可能的值。你可以把可選值當(dāng)作一個包含零個或者一個值的集合掉缺,這樣 map 要么在零值的情況下不做處理卜录,要么在有值的時候會對其進(jìn)行轉(zhuǎn)換。

當(dāng)你想要的就是一個可選值結(jié)果時眶明,可選值 map 就非常有用艰毒。設(shè)想你想要為數(shù)組實(shí)現(xiàn)一個變種的 reduce 方法,這個方法不接受初始值搜囱,而是直接使用數(shù)組中的首個元素作為初始值.

[1, 2, 3, 4].reduce(+)

因?yàn)閿?shù)組可能會是空的丑瞧,這種情況下沒有初始值,結(jié)果只能是 nil蜀肘,所以這個結(jié)果應(yīng)當(dāng)是一個可選值绊汹。你可能會這樣來實(shí)現(xiàn)它:

extension Array {
  func reduce(combine: (Element, Element) -> Element) -> Element? {
  // 如果數(shù)組為空,self.first 將是 nil
  guard let fst = first else { return nil }
  return self.dropFirst().reduce(fst, combine: combine)
  }
}

因?yàn)榭蛇x值為 nil 時扮宠,可選值的 map 也會返回 nil西乖,所以我們可以使用不包含 guard 的單 return 形式來重寫 reduce:

extension Array {
  func reduce(combine: (Element, Element) -> Element) -> Element? {
    return first.map {
      self.dropFirst().reduce($0, combine: combine)
    }
  }
}

鑒于可選值 map 與集合的 map 的相似性,可選值 map 的實(shí)現(xiàn)和集合 map 也很類似:

extension Optional {
  func map<U>(transform: Wrapped -> U) -> U? {
    if let value = self {
      return transform(value)
    }
    return nil
  }
}

可選值 flatMap

如果你的序列中包含可選值坛增,可能你會只對那些非 nil 值感興趣获雕。實(shí)際上,你可以忽略掉那些 nil 值轿偎。

設(shè)想你需要處理一個字符串?dāng)?shù)組中的數(shù)字典鸡。在有可選值模式匹配時被廓,用 for 循環(huán)可以很簡單地就實(shí)現(xiàn):

let numbers = ["1", "2", "3", "foo"]
var sum = 0
for case let i? in numbers.map({ Int($0) }) {
  sum += i
}

你可能也會想用 ?? 來把 nil 替換成 0:

numbers.map { Int($0) }.reduce(0) { $0 + ($1 ?? 0) }

實(shí)際上坏晦,你想要的版本應(yīng)該是一個可以將那些 nil 過濾出去并將非 nil 值進(jìn)行解包的 map。標(biāo)準(zhǔn)庫中序列的 flatMap 正是你想要的:

numbers.flatMap { Int($0) }.reduce(0, combine: +)

可選值判等和比較

在判等時你不需要關(guān)心一個值是不是 nil,你只需要檢查它是否包含某個 (非 nil 的) 特定值即可:

if regex.characters.first == "^" {
// 只匹配字符串開頭
}

上面的代碼之所以能工作主要基于兩點(diǎn)昆婿。首先球碉,== 有一個接受兩個可選值的版本,它的實(shí)現(xiàn)類似這樣:

func ==<T: Equatable>(lhs: T?, rhs: T?) -> Bool {
  switch (lhs, rhs) {
    case (nil, nil): return true
    case let (x?, y?): return x == y
    case (_?, nil), (nil, _?): return false
  }
}

強(qiáng)制解包的時機(jī)

上面提到的例子都用了很利索的方式來解包可選值仓蛆,什么時候你應(yīng)該用感嘆號 (!) 這個強(qiáng)制解包運(yùn)算符呢睁冬?

當(dāng)你能確定你的某個值不可能是 nil 時可以使用嘆號,你應(yīng)當(dāng)會希望如果它不巧意外地是 nil 的話看疙,這句程序直接掛掉豆拨。

  func flatten<S: SequenceType, T where S.Generator.Element == T?>(source: S) -> [T]{
    return Array(source.lazy.filter { $0 != nil }.map { $0! })
  }

這里,因?yàn)樵?filter 的時候已經(jīng)把所有 nil 元素過濾出去了能庆,所以 map 的時候沒有任何可能會出現(xiàn) $0! 碰到 nil 值的情況施禾。
不過使用強(qiáng)制解包還是很罕見的。

第二個例子搁胆,下面這段代碼會根據(jù)特定的條件來從字典中找到值滿足這個條件的對應(yīng)的所有的鍵:

let ages = [
  "Tim": 53, "Angela": 54, "Craig": 44,
  "Jony": 47, "Chris": 37, "Michael": 34,
]

let people = ages
.keys
.filter { name in ages[name]! < 50 }
.sort()

這里使用 ! 非常安全 — 因?yàn)樗械逆I都是來源于字典的弥搞,所以在字典中找不到這個鍵是不可能的。

改進(jìn)強(qiáng)制解包的錯誤信息

其實(shí)渠旁,你可能會留一個注釋來提醒為什么這里要使用強(qiáng)制解包攀例。那為什么不把這個注釋直接作為錯誤信息呢?這里我們加了一個 !! 操作符顾腊,它將強(qiáng)制解包和一個更具有描述性質(zhì)的錯誤信息結(jié)合在一起粤铭,當(dāng)程序意外退出時,這個信息也會被打印出來:

infix operator !! { }
func !! <T>(wrapped: T?, @autoclosure failureText: ()->String) -> T {
  if let x = wrapped { return x }
  fatalError(failureText())
}

let s = "foo"
let i = Int(s) !! "Expecting integer, got \"\(s)\"”

@autoclosure 注解確保了我們只在需要的時候會執(zhí)行操作符右側(cè)的語句杂靶。

在調(diào)試版本中進(jìn)行斷言

通常承耿,你可能會選擇在調(diào)試版本或者測試版本中進(jìn)行斷言,讓程序崩潰伪煤,但是在最終產(chǎn)品中加袋,你可能會把它替換成像是零或者空數(shù)組這樣的默認(rèn)值。

我們可以實(shí)現(xiàn)一個疑問感嘆號 !? 操作符來代表這個行為抱既。我們將這個操作符定義為對失敗的解包進(jìn)行斷言职烧,并且在斷言不觸發(fā)的發(fā)布版本中將值替換為默認(rèn)值:

infix operator !? { }
func !?<T: IntegerLiteralConvertible>(wrapped: T?, @autoclosure failureText: ()->String) -> T{
    assert(wrapped != nil, failureText())
    return wrapped ?? 0
}

現(xiàn)在,下面的代碼將在調(diào)試時觸發(fā)斷言防泵,但是在發(fā)布版本中打印 0:

let i = Int(s) !? "Expecting integer, got \"\(s)\""

對于返回 Void 的函數(shù)蚀之,使用可選鏈進(jìn)行調(diào)用時將返回 Void?。利用這一點(diǎn)捷泞,你可以寫一個非泛型的版本來檢測一個可選鏈調(diào)用碰到 nil足删,且并沒有進(jìn)行完操作的情況:

func !?(wrapped: ()?, @autoclosure failureText: ()->String) {
  assert(wrapped != nil, failureText)
}
var output: String? = nil
output?.write("something") !? "Wasn't expecting chained nil here”

想要掛起一個操作我們有三種方式。首先锁右,fatalError 將接受一條信息失受,并且無條件地停止操作讶泰。第二種選擇,使用 assert 來檢查條件拂到,當(dāng)條件結(jié)果為 false 時痪署,停止執(zhí)行并輸出信息。在發(fā)布版本中兄旬,assert 會被移除掉狼犯,條件不會被檢測,操作也永遠(yuǎn)不會掛起领铐。第三種方式是使用 precondition悯森,它和 assert 比較類型,但是在發(fā)布版本中它不會被移除绪撵,也就是說呐馆,只要條件被判定為 false,執(zhí)行就會被停止莲兢。

多災(zāi)多難的隱式可選值

隱式可選值是那些不論何時你使用它們的時候就自動強(qiáng)制解包的可選值汹来。別搞錯了,它們依然是可選值改艇,現(xiàn)在你已經(jīng)知道了當(dāng)可選值是 nil 的時候強(qiáng)制解包會造成應(yīng)用崩潰收班,那你到底為什么會要用到隱式可選值呢?實(shí)際上有兩個原因:

  • 暫時來說谒兄,你可能還需要到 Objective-C 里去調(diào)用那些沒有檢查返回是否存在的代碼摔桦。
  • 因?yàn)橐粋€值只是很短暫地為 nil,在一段時間后承疲,它就再也不會是 nil邻耕。

隱式可選值行為

因?yàn)殡[式可選值會盡可能地隱藏它們的可選值特性,所以它們在行為上也有一些不一樣燕鸽。

func increment(inout x: Int) {
  x += 1
}

// 普通的 Int
var i = 1
// 將 i 增加為 2
increment(&i)
// 隱式解包的 Int
var j: Int! = 1
// 錯誤:cannot invoke 'increment' with an argument list of type '(inout Int!)'
increment(&j)

總結(jié)

在處理有可能是 nil 的值的時候兄世,可選值會非常有用。相比于使用像是 NSNotFound 這樣的魔法數(shù)啊研,我們可以用 nil 來代表一個值為空御滩。Swift 中有很多內(nèi)置的特性可以處理可選值,所以你能夠避免進(jìn)行強(qiáng)制解包党远。隱式解包可選值在與遺留代碼協(xié)同工作時會有用削解,但是在有可能的情況下還是應(yīng)該盡可能使用普通的可選值。最后沟娱,如果你需要比單個可選值更多的信息 (比如氛驮,在結(jié)果不存在時你可能需要一個錯誤信息提示),你可以使用拋出錯誤的方法济似。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末矫废,一起剝皮案震驚了整個濱河市盏缤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌磷脯,老刑警劉巖蛾找,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娩脾,死亡現(xiàn)場離奇詭異赵誓,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)柿赊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進(jìn)店門俩功,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人碰声,你說我怎么就攤上這事诡蜓。” “怎么了胰挑?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵蔓罚,是天一觀的道長。 經(jīng)常有香客問我瞻颂,道長豺谈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任贡这,我火速辦了婚禮茬末,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘盖矫。我一直安慰自己丽惭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布辈双。 她就那樣靜靜地躺著责掏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪湃望。 梳的紋絲不亂的頭發(fā)上拷橘,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天,我揣著相機(jī)與錄音喜爷,去河邊找鬼冗疮。 笑死,一個胖子當(dāng)著我的面吹牛檩帐,可吹牛的內(nèi)容都是我干的术幔。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼湃密,長吁一口氣:“原來是場噩夢啊……” “哼诅挑!你這毒婦竟也來了四敞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤拔妥,失蹤者是張志新(化名)和其女友劉穎忿危,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體没龙,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铺厨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了硬纤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片解滓。...
    茶點(diǎn)故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖筝家,靈堂內(nèi)的尸體忽然破棺而出洼裤,到底是詐尸還是另有隱情,我是刑警寧澤溪王,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布腮鞍,位于F島的核電站,受9級特大地震影響莹菱,放射性物質(zhì)發(fā)生泄漏移国。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一芒珠、第九天 我趴在偏房一處隱蔽的房頂上張望桥狡。 院中可真熱鬧,春花似錦皱卓、人聲如沸裹芝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嫂易。三九已至,卻和暖如春掐禁,著一層夾襖步出監(jiān)牢的瞬間怜械,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工傅事, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缕允,地道東北人。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓蹭越,卻偏偏與公主長得像障本,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容