Swift 4 新特性——What's New in Swift 4?

? ? 本文翻譯自raywenderlich.com中的文章《What’s New in Swift 4?》,由于本人水平有限,翻譯中不準確或者有錯誤的地方,敬請諒解和指正。

? ? 提示:本教程使用集成在 Xcode 9 beta 1 中Swift 4 版本。

? ? Swift 4 是蘋果公司計劃于2017年秋季發布的最新主要版本,它主要關注與Swift 3 的兼容性,以及繼續向ABI穩定邁進。

? ? 皮皮Swift 4,我們走

? ? Swift 4 包含于Xcode 9中。你可以從蘋果公司的開發者頁面下載最新的Xcode 9(你必須有一個開發者賬號)。每個Xcode版本將會包含Swift 4當時版本的快照。

? ? 當你閱讀時,你會發現鏈接都是SE-xxxx形式。這些鏈接將會為你展示相關的Swift演化提案。如果你想深入學習某個專題,可以翻閱它們。

? ? 我推薦在playground中嘗試每個 Swift 4 的特性或更新。這將幫助強化你頭腦中的知識,賦予你深入學習每個專題的能力。擴展他們、打破它們,與playground中的例子斗,其樂無窮。

? ? 提示:這篇文章將隨著每個Xcode beta版本更新。如果你使用不同的快照,不能保證代碼可以運行。

? ? 移植到 Swift 4

? ? Swift 3 移植到 4 要比 2.2 到 3容易一些。總的來說,大多數變化都是新增的,不需要大量的個人情懷(personal touch)。因此,Swift移植工具將為你完成大部分的修改工作量。

? ? Xcode 9同時支持 Swift 4 以及Swift 3 的中間版本 Swift 3.2。項目中的target既可以是 Swift 3.2 也可以是 Swift 4,讓你在需要的時候一點一點移植。轉換到 Swift 3.2不是完全沒有工作量,你需要更新部分代碼來兼容新的SDK,再者因為 Swift 還沒有ABI穩定,所以你需要用Xcode 9 重新編譯依賴關系。

? ? 當你準備移植到 Swift 4,Xcode再次提供了移植工具幫助你。Xcode中,你可以使用“Edit/Convert/To Current Swift Syntax”啟動轉換工具。

? ? 選擇了想要轉換的target之后,Xcode將提示你Objective-C推斷的偏好設置,選擇推薦選項以通過限制推斷來減少二進制大小(更多這個專題,查看下面的限制@objc推斷)

推斷偏好設置

? ? 為了更好的理解你代碼中將會出現的變化,我們先來看看 Swift 4中的API 變化。

? ? API變化

在轉入Swift 4 新增特性之前,讓我們先看下已有API的變化或更新。

? ? 字符串Strings

? ? Swift 4 中String類收到頗多關愛,這份提案包含很多變化,來看看最大變化【SE-0163】。

假如你感覺懷舊,strings像Swift 2.0之前那樣再次成為集合。這一變化移除了String必須的character數組。現在你可以直接在String對象上迭代。


let galaxy = "Milky Way ??"

for char in galaxy {

print(char)

}


? ? 你不僅可以對String做邏輯迭代,還可以得到Sequence和Collection中的額外特性。


galaxy.count? ? ? // 11

galaxy.isEmpty? ? // false

galaxy.dropFirst() // "ilky Way ??"

String(galaxy.reversed()) // "?? yaW ykliM"

// Filter out any none ASCII characters

galaxy.filter { char in

let isASCII = char.unicodeScalars.reduce(true, { $0 && $1.isASCII })

return isASCII

} // "Milky Way "


? ? 上面的ASCII例子闡述了對Character的小改進。你可以直接訪問Character的UnicodeScalarView屬性。之前你需要生成一個新String【SE-0178】。

? ? 另外一個新增特性是StringProtocol。它聲明了大部分String中聲明的功能。這樣做是為改進slice如何工作。Swift 4 增加了 Substring類型來引用String的子序列。

? ? String和Substring都實現StringProtocol協議,所以他們功能的幾乎完全相同:


// Grab a subsequence of String

let endIndex = galaxy.index(galaxy.startIndex, offsetBy: 3)

var milkSubstring = galaxy[galaxy.startIndex...endIndex]? // "Milk"

type(of: milkSubstring)? // Substring.Type

// Concatenate a String onto a Substring

milkSubstring += "??"? ? // "Milk??"

// Create a String from a Substring

let milkString = String(milkSubstring) // "Milk??"


另外一個值得點贊的改進是,String如何表示字母簇。這一決議來源于Unicode 9 的適配。原先,多編碼點的unicode字符長度大于1,常見于emoji表情。下面是一些常見的例子:


"?????".count // Now: 1, Before: 2

"????".count // Now: 1, Before: 2

"???????????".count // Now: 1, Before, 4


? ? 這只是String Manifesto中的一部分變化,你可以在String Manifesto讀到所有這些的原始動機,以及未來可預見的解決提案。

? ? 字典和集合

? ? 集合類型不斷發展,Set和Dictionary不總是最直觀的。幸運的是,Swift 團隊給了他們很多的關注【SE-0165】

? ? 基于序列的初始化

? ? 第一項能力就是用鍵值對序列創建字典:


let nearestStarNames = ["Proxima Centauri", "Alpha Centauri A", "Alpha Centauri B", "Barnard's Star", "Wolf 359"]

let nearestStarDistances = [4.24, 4.37, 4.37, 5.96, 7.78]

// Dictionary from sequence of keys-values

let starDistanceDict = Dictionary(uniqueKeysWithValues: zip(nearestStarNames, nearestStarDistances))

// ["Wolf 359": 7.78, "Alpha Centauri B": 4.37, "Proxima Centauri": 4.24, "Alpha Centauri A": 4.37, "Barnard's Star": 5.96]


? ? 重復Key解決方案

? ? 你現在可以在初始化字典時,使用任意方法處理重復key問題。這可以避免“無提示的改寫鍵值對”。


// Random vote of people's favorite stars

let favoriteStarVotes = ["Alpha Centauri A", "Wolf 359", "Alpha Centauri A", "Barnard's Star"]

// Merging keys with closure for conflicts

let mergedKeysAndValues = Dictionary(zip(favoriteStarVotes, repeatElement(1, count: favoriteStarVotes.count)), uniquingKeysWith: +) // ["Barnard's Star": 1, "Alpha Centauri A": 2, "Wolf 359": 1]


? ? ?上面的代碼使用zip和“+”號,將重復key對應的值相加。

? ? 提示:如果你不熟悉zip,你可以快速的在蘋果的Swift文檔中學習。

? ? 篩選

? ? 字典和集合(Set)現在都獲取了篩選能力,將篩選結果放入原始類型的新對象中。


// Filtering results into dictionary rather than array of tuples

let closeStars = starDistanceDict.filter { $0.value < 5.0 }

closeStars // Dictionary: ["Proxima Centauri": 4.24, "Alpha Centauri A": 4.37, "Alpha Centauri B": 4.37]


? ? 字典映射

字典獲得了一個非常有用的方法,可以直接映射它的值:


// Mapping values directly resulting in a dictionary

let mappedCloseStars = closeStars.mapValues { "\($0)" }

mappedCloseStars // ["Proxima Centauri": "4.24", "Alpha Centauri A": "4.37", "Alpha Centauri B": "4.37"]


? ? ?字典缺省值

? ? 訪問字典中的值時,常見的實踐方法,是使用??操作符,當值為nil時賦予缺省值。在 Swift 4 中,這項操作變得簡潔,允許你在代碼行上“耍花槍”。


// Subscript with a default value

let siriusDistance = mappedCloseStars["Wolf 359", default: "unknown"] // "unknown"

// Subscript with a default value used for mutating

var starWordsCount: [String: Int] = [:]

for starName in nearestStarNames {

let numWords = starName.split(separator: " ").count

starWordsCount[starName, default: 0] += numWords // Amazing

}

starWordsCount // ["Wolf 359": 2, "Alpha Centauri B": 3, "Proxima Centauri": 2, "Alpha Centauri A": 3, "Barnard's Star": 2]


? ? 以前這種變換都必須包裹在臃腫的 if-let 語句中,在 Swift 4 中,只需簡單一行代碼。

? ? 字典組

? ? 另外一個很有用的新增特性是,用序列初始化字典并把他們組合在一起:


// Grouping sequences by computed key

let starsByFirstLetter = Dictionary(grouping: nearestStarNames) { $0.first! }

// ["B": ["Barnard's Star"], "A": ["Alpha Centauri A", "Alpha Centauri B"], "W": ["Wolf 359"], "P": ["Proxima Centauri"]]


這在處理特殊形式的數據時變得很便利。

? ? 預留容量

? ? 序列和字典都有了顯式預留容量的能力。


// Improved Set/Dictionary capacity reservation

starWordsCount.capacity? // 6

starWordsCount.reserveCapacity(20) // reserves at _least_ 20 elements of capacity

starWordsCount.capacity // 24


? ? 對序列和字典重新分配內存空間,是非常耗費資源的操作。當你知道需要多少數據時,使用reserveCapacity(_:)是提升性能的簡單方法。

? ? 這包含了很多信息,所以查看這兩個類型的文檔,找到優化代碼的方法。

? ? 私有訪問修飾符

? ? 有些人不喜歡 Swift 3 中新增的 fileprivate。理論上,它很贊,但實際上它的用法讓人糊涂。這樣做的目的是,在成員內部使用private,而在相同文件中想要在成員中分享訪問權限則使用 fileprivate。

? ? 這個問題源自于,Swift鼓勵使用擴展讓代碼邏輯分組。擴展被看做是原始成員聲明作用域的外圍,使得擴展需要 fileprivate。

? ? Swift 4 認識到了在類型及其擴展之間分享相同訪問作用域的原始動機,只有在相同的源文件中才起作用【SE-0169】:


struct SpaceCraft {

private let warpCode: String

init(warpCode: String) {

self.warpCode = warpCode

}

}

extension SpaceCraft {

func goToWarpSpeed(warpCode: String) {

if warpCode == self.warpCode { // Error in Swift 3 unless warpCode is fileprivate

print("Do it Scotty!")

}

}

}

let enterprise = SpaceCraft(warpCode: "KirkIsCool")

//enterprise.warpCode? // error: 'warpCode' is inaccessible due to 'private' protection level

enterprise.goToWarpSpeed(warpCode: "KirkIsCool") // "Do it Scotty!"


? ? 這樣,fileprivate就回歸了本來的目的,而不是作為代碼組織結構的繃帶。

? ? 新增API

? ? 現在讓我們看看 Swift 4 的亮瞎雙眼的新特性。這些特性不會打破已有代碼,因為他們都是簡單新增。

? ? 歸檔和序列化

? ? 講到這里,序列化和歸檔自定義類型,你需要一些套路。對于類,你需要創建NSObject的子類、實現NSCoding協議。

? ? struct和enum等值類型則需要一些小技巧,像創建能夠繼承NSObject和NSCoding的子類。

? ? Swift 4 引入這三種類型的序列化來解決這個問題【SE-0166】。


struct CuriosityLog: Codable {

enum Discovery: String, Codable {

case rock, water, martian

}

var sol: Int

var discoveries: [Discovery]

}

// Create a log entry for Mars sol 42

let logSol42 = CuriosityLog(sol: 42, discoveries: [.rock, .rock, .rock, .rock])


? ? ?從例子可以看出,只要實現 Codable 協議就可以使得Swift類型可編碼和解碼。如果所有屬性都是Codable,編譯器自動生成協議的實現。

? ? 真正對一個對象編碼,你需要將它傳給一個編碼器。Swift 4 正在積極推進Swift編碼器。根據不同的模式給對象編碼。【SE-0167】(注意:這個提案的部分內容還在修訂中)。


let jsonEncoder = JSONEncoder() // One currently available encoder

// Encode the data

let jsonData = try jsonEncoder.encode(logSol42)

// Create a String from the data

let jsonString = String(data: jsonData, encoding: .utf8) // "{"sol":42,"discoveries":["rock","rock","rock","rock"]}"


? ? 上面代碼將一個對象自動編碼為JSON對象。注意檢查JSONEncoder暴露出來的屬性以便自定義輸出結果。

? ? 最后是將數據解碼到一個具體對象:


let jsonDecoder = JSONDecoder() // Pair decoder to JSONEncoder

// Attempt to decode the data to a CuriosityLog object

let decodedLog = try jsonDecoder.decode(CuriosityLog.self, from: jsonData)

decodedLog.sol? ? ? ? // 42

decodedLog.discoveries // [rock, rock, rock, rock]


? ? 有了 Swift 4,編碼和解碼變得類型安全,不需要依賴@objc協議的限制。

? ? 鍵值編碼

? ? 因為函數在Swift中是閉包,所以你可以持有函數的引用而不調用它們。但做不到的是,持有屬性的引用而不實際訪問屬性下的數據。

? ? Swift 4 新增了讓人興奮的特性,可以引用類型的keypath來get/set實例的底層數據。


struct Lightsaber {

enum Color {

case blue, green, red

}

let color: Color

}

class ForceUser {

var name: String

var lightsaber: Lightsaber

var master: ForceUser?

init(name: String, lightsaber: Lightsaber, master: ForceUser? = nil) {

self.name = name

self.lightsaber = lightsaber

self.master = master

}

}

let sidious = ForceUser(name: "Darth Sidious", lightsaber: Lightsaber(color: .red))

let obiwan = ForceUser(name: "Obi-Wan Kenobi", lightsaber: Lightsaber(color: .blue))

let anakin = ForceUser(name: "Anakin Skywalker", lightsaber: Lightsaber(color: .blue), master: obiwan)


? ? 這里創建了一些force的實例,設置了名字、光劍和主人。要創建key path,你只需要在屬性名前加上右斜線:


// Create reference to the ForceUser.name key path

let nameKeyPath = \ForceUser.name

// Access the value from key path on instance

let obiwanName = obiwan[keyPath: nameKeyPath]? // "Obi-Wan Kenobi"


? ? ?上面例子中,為ForceUser的name屬性創建了一個key path,你可以把它傳遞給keyPath參數。下面是一些例子,使用key path擴展到子對象、設置屬性等。


// Use keypath directly inline and to drill down to sub objects

let anakinSaberColor = anakin[keyPath: \ForceUser.lightsaber.color]? // blue

// Access a property on the object returned by key path

let masterKeyPath = \ForceUser.master

let anakinMasterName = anakin[keyPath: masterKeyPath]?.name? // "Obi-Wan Kenobi"

// Change Anakin to the dark side using key path as a setter

anakin[keyPath: masterKeyPath] = sidious

anakin.master?.name // Darth Sidious

// Note: not currently working, but works in some situations

// Append a key path to an existing path

//let masterNameKeyPath = masterKeyPath.appending(path: \ForceUser.name)

//anakin[keyPath: masterKeyPath] // "Darth Sidious"


? ? Swift中key path的優雅之處在于它們是強類型的,不再有Objective-C的雜亂。

? ? 多行String字面量

? ? 很多編程語言的特性中包含創建多行字符串字面量的能力。Swift 4用三個引號"""包裹文本,實現了這個簡單但有用的語法。


let star = "??"

let introString = """

A long time ago in a galaxy far,

far away....

You could write multi-lined strings

without "escaping" single quotes.

The indentation of the closing quotes

below deside where the text line

begins.

You can even dynamically add values

from properties: \(star)

"""

print(introString) // prints the string exactly as written above with the value of star


? ? 這在創建XML/JSON信息或者創建長格式文本時,非常有用。

? ? 單邊范圍

? ? 為減少冗長、提高可讀性,標準庫可以使用單邊范圍確定開始、結束索引。這使得從集合截取變得方便。


// Collection Subscript

var planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]

let outsideAsteroidBelt = planets[4...] // Before: planets[4..


? ?能看到,單邊范圍減少了顯示指定開始、結束索引的必要。

? ? 無窮序列

? ? 當開始索引是可數類型時,你可以創建一個無窮序列:


// Infinite range: 1...infinity

var numberedPlanets = Array(zip(1..., planets))

print(numberedPlanets) // [(1, "Mercury"), (2, "Venus"), ..., (8, "Neptune")]

planets.append("Pluto")

numberedPlanets = Array(zip(1..., planets))

print(numberedPlanets) // [(1, "Mercury"), (2, "Venus"), ..., (9, "Pluto")]


? ? 模式匹配

? ? 單邊范圍的另一個用途是模式匹配:


// Pattern matching

func temperature(planetNumber: Int) {

switch planetNumber {

case ...2: // anything less than or equal to 2

print("Too hot")

case 4...: // anything greater than or equal to 4

print("Too cold")

default:

print("Justtttt right")

}

}

temperature(planetNumber: 3) // Earth


? ? 泛型下標

? ? 下標是訪問數據類型的重要組成部分,同時也非常直觀。為提升這種有用性,下標現在可支持泛型【SE-0148】:


struct GenericDictionary<Key : Hashable, Value>{??

? ? private var data: [Key: Value]??

? ? init(data: [Key: Value]) {? ??

? ? ? ? self.data = data??

? ? }??

? ? subscript<T>(key: Key) -> T? {

? ? ? ? return data[key] as? T

? ? }

}


? ? 例子中返回類型為泛型,你可以這樣使用泛型下標:


// Dictionary of type: [String: Any]

var earthData = GenericDictionary(data: ["name": "Earth", "population": 7500000000, "moons": 1])

// Automatically infers return type without "as? String"

let name: String? = earthData["name"]

// Automatically infers return type without "as? Int"

let population: Int? = earthData["population"]


? ?不僅返回類型可以為泛型,下標類型也可以為泛型:


extension GenericDictionary {? subscript(keys: Keys) -> [Value] where Keys.Iterator.Element == Key {

var values: [Value] = []

for key in keys {

if let value = data[key] {

values.append(value)

}

}

return values

}

}

// Array subscript value

let nameAndMoons = earthData[["moons", "name"]]? ? ? ? // [1, "Earth"]

// Set subscript value

let nameAndMoons2 = earthData[Set(["moons", "name"])]? // [1, "Earth"]


? ? 本例中,你可以傳入兩個不同的序列類型(數組和集合),得到相關值的數組。

? ? 雜項

? ? MutableCollection現在有了可變方法swapAt(_:_:),正向看起來的那樣,它交換給定數組中的值。


// Very basic bubble sort with an in-place swap

func bubbleSort(_ array: [T]) -> [T] {??

? ? var sortedArray = array??

? ? for i in 0..sortedArray[j] {

? ? ? ? sortedArray.swapAt(j-1, j) // New MutableCollection method

? ? ?}

? ?}

}

return sortedArray

}

bubbleSort([4, 3, 2, 1, 0]) // [0, 1, 2, 3, 4]


? ? ? 關聯類型約束

可以通過where語句包含關聯類型約束【SE=0142】:


protocol MyProtocol {

? ? associatedtype Element

? ? associatedtype SubSequence : Sequence where SubSequence.Iterator.Element == Iterator.Element

}


? ? 使用協議約束,很多associatedtype聲明可以直接包含自身的值,而不需要其他套路。

? ? 類和協議的存在性

? ? 區分Objective-C和Swift的重要特性就是,Swift可以定義一個類型,既遵從一個類又遵從一些協議【SE-0156】:


protocol MyProtocol { }

class View { }

class ViewSubclass: View, MyProtocol { }

class MyClass {

var delegate: (View & MyProtocol)?

}

let myClass = MyClass()

//myClass.delegate = View() // error: cannot assign value of type 'View' to type '(View & MyProtocol)?'

myClass.delegate = ViewSubclass()


? ? 限制@objc推斷

? ? 要提供Swift API給Objective-C,你要使用@objc編譯器屬性。很多情況下,Swift編譯器可以為你推斷。推斷的三個主要問題在于:

? ? 1、二進制大小大幅度增加的隱患

? ? 2、不確定@objc何時被推斷。

? ? 3、無意間造成和Objective-C函數沖突的可能性增加。

Swift 4拿出大板斧,限制了@objc推斷【SE-0160】。這意味著,當你需要Objective-C全部動態分發能力時,必須顯式使用@objc。

? ? NSNumber橋接

? ?NSNumber和Swift numbers之間有很多惱人的惡臭,縈繞在語言周圍太長時間了。幸運的是,Swift解決了它們【SE-0170】。


let n = NSNumber(value: 999)

let v = n as? UInt8 // Swift 4: nil, Swift 3: 231


? ? Swift 3 中的奇怪結果表明,如果數字溢出,它直接從0開始。這個例子中,999 % 2^8 = 231。

? ? Swift 4解決了這個問題,只有當數字可以被安全轉換時,才做可選類型轉換。

? ? Swift包管理器

? ? 過去幾個月,Swift包管理器已經有一定數量的更新,其中大的變更包括:

? ? 1、根據分支或者提交哈希進行源碼以來

? ? 2、可接受包版本的更多控制

? ? 3、替換不直觀的命令,使用一些更常用的解決模式

? ? 4、使用編譯器定義Swift版本的能力

? ? 5、為每個target指定源文件路徑

? ? 這些大變化都是Swift包管理器需要做的,SPM還有很長的路要走,我們都可以通過提案,幫助它更好發展。

? ? 一直在路上

? ? 在寫這篇文章時,仍有15分已接受的提案在排隊。如果你想知道接下來會發生什么,查看Swift演化提案、選擇“Accepted”。

? ? 路怎么走,你們自己挑?

? ? Swift語言這幾年不斷發育成熟。提案進程和社區參與,使得大家能夠跟蹤語言變化,也使得我們每個人都可以直接影響語言的演化。

? ? 上面的 Swift 4 變化,我們終于發現ABI穩定就在下一個轉角。Swift升級的陣痛在變小。構建性能和工具都大幅度提升。在蘋果生態外使用Swift變得越發可行了。設想一下,我們離一個直觀的實現,還差一小部分的String重寫。

? ? Swift還會迎來很多改變。跟上每個變化的節奏,查看以下資源:

1、Swift變更日志

2、Swift演化提案

3、WWDC 2017 視頻

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,238評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,430評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,134評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,893評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,653評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,136評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,212評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,372評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,888評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,738評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,482評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,179評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,588評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,829評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,610評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,916評論 2 372

推薦閱讀更多精彩內容