Swift 5.1 介紹

文章比較長诺舔,不想看完的可以看總結(jié)鳖昌。

總結(jié)

知道 SwiftUI 的人,都會(huì)很想使用這個(gè)框架低飒,但是由于只能在 iOS 13以及以后的系統(tǒng)使用许昨,所以會(huì)很痛心。像我就覺得為什么 SwiftUI 不開源叭焐蕖糕档!開源多好,將底層渲染替換一下拌喉,就能用 SwiftUI 在 Android 上編程了速那!對(duì) Flutter 也有沖擊俐银。但是就像我在文中說的 Swift5.1 引入的二進(jìn)制庫穩(wěn)定導(dǎo)致,蘋果可以不開源我們就能使用 SwiftUI.framework琅坡。(好痛心??悉患,寧愿不要二進(jìn)制庫穩(wěn)定!)

幸運(yùn)的是榆俺,SwiftUI 之所以能夠出現(xiàn)售躁,很大程度上得益于 Swift5.1 引入的新特性屬性包裝器(文中有介紹,但是坑爹的簡書貌似不支持 Markdown 文內(nèi)鏈接)以及到目前為止官方都沒有披露的 @_functionBuilder茴晋。雖然由于需要支持 iOS 13 以前系統(tǒng)的原因不能使用 SwiftUI陪捷,但是只要我們使用 Swift5.1 就可以使用 Swift 的這兩個(gè)新特性。這樣一來诺擅,在 iOS 13 以前我們可以開發(fā)另一套界面描述 DSL市袖,同時(shí)也能夠很容易支持?jǐn)?shù)據(jù)的雙向綁定,如此一來烁涌,從 Swift5.1 開始 iOS 開發(fā)人員編寫界面的方式會(huì)有很大的變化苍碟,從以前的命令式過渡到現(xiàn)在的聲明式。畢竟 SwiftUI 的最大討好點(diǎn)就是聲明式的界面編寫撮执。(從這一點(diǎn)來說我反而比較擔(dān)心 SwiftUI 可能并不會(huì)被開發(fā)者青睞微峰。因?yàn)楫吘共恢С?iOS 13 以前的系統(tǒng),而且使用 Swift5.1 也很容易的實(shí)現(xiàn)聲明式的 UI 框架抒钱,不過考慮到 SwiftUI 是跨 iOS 和 macOS 平臺(tái)的蜓肆,所以,可能并不是我想的那么不被青睞)

最后一句總結(jié)谋币,由于屬性包裝器以及 @_functionBuilder 的存在仗扬,從 Swift5.1 開始,Swift 成為一門真正的支持高效 UI 編程的語言蕾额,一門高效的用于開發(fā) DSL 的語言早芭,一門更加易用的語言酒繁!

前言

視頻地址:What's New in Swift
本文介紹視頻中的主要內(nèi)容治宣。(以及夾帶一些私貨)

二進(jìn)制庫穩(wěn)定

ABI 穩(wěn)定

Swift 5 帶來了 ABI 的穩(wěn)定。但是用 Swift 寫的二進(jìn)制框架并沒有穩(wěn)定化借。

先說一下什么是 ABI(Application Binary Interface)秤涩,打個(gè)比方帜乞,代碼中調(diào)用一個(gè)函數(shù),需要傳入幾個(gè)參數(shù)筐眷,調(diào)用結(jié)束需要返回結(jié)果黎烈,編譯器如何將參數(shù)傳給被調(diào)用者,以及如何將結(jié)果返回給調(diào)用方,就是 ABI 涉及的內(nèi)容照棋。ABI 不穩(wěn)定意味則资溃,如何傳參數(shù)和返回參數(shù)還沒有最終確定(當(dāng)然這只是舉例,實(shí)際的編譯器實(shí)現(xiàn)很復(fù)雜烈炭,ABI 涉及很多方面)溶锭。ABI 不穩(wěn)定導(dǎo)致的結(jié)果就是,使用 Swift 5 以前的版本編寫 iOS app符隙,Xcode 需要同時(shí)將 ABI 相關(guān)的 Swift 核心庫編譯到你的 app 中趴捅,直觀的感受就是你的 ipa 包變大了。

?? iOS 12.2 系統(tǒng)內(nèi)置 Swift 核心庫所以 Swift5 編寫的 app 不需要內(nèi)置 Swift 核心庫了霹疫,但是 Xcode 在打包的時(shí)候仍然會(huì)將 Swift5 核心庫集成到 ipa 里面拱绑,這樣 Swift5 寫的 app 就能運(yùn)行在 iOS12.2 之前系統(tǒng)。只是對(duì)于 iOS 12.2 及以后的系統(tǒng)安裝該 app 的時(shí)候丽蝎,系統(tǒng)不需要下載 app 中 Swift 的核心庫了猎拨,減少了下載體積和安裝大小。(同時(shí) app 啟動(dòng)也變快了屠阻,因?yàn)橄到y(tǒng) dyld3 緩存了對(duì)應(yīng)的 Swift 核心庫红省,官方說能帶來 5% 的啟動(dòng)速度提升)

??? 問題來了,如果你用 Swift5.1 編寫的 app国觉,安裝在 iOS12.2 上面吧恃,需要下載并且安裝 Swift5.1 對(duì)應(yīng)的核心庫嗎?

ABI 穩(wěn)定之后就是模塊穩(wěn)定了蛉加。

模塊穩(wěn)定

模塊穩(wěn)定指的是,用 Swift5.1 以前編寫的模塊缸逃,如果想要提供給第三方的話针饥,需要把這個(gè)模塊的代碼也提供第三方,這樣他們才能使用這個(gè)模塊需频。當(dāng)然這對(duì)于某些模塊的開發(fā)者來說是不可接受的(畢竟是把模塊的源代碼給別人了)丁眼。模塊庫穩(wěn)定指的是,用 Swift5.1昭殉,庫開發(fā)者不需要提供源代碼給第三方了苞七,只需要提供一個(gè)類似頭文件的東西給第三方,第三方就可以使用了挪丢。

順便簡單說一下 Swift 是如何使用模塊蹂风。如下圖所示:

Swift5及以前.png

可以知道 Swift5.1 以前,在 Foo.swift 中 import MyFramework 的話乾蓬,Swift 會(huì)自動(dòng)在 Foo.swift 中導(dǎo)入 MyFramework 的“頭文件”亦即 swiftmodule 文件惠啄,這個(gè)文件是編譯器在編譯 MyFramework 的時(shí)候生成的。如此一來,就可以在 Foo.swift 中任意使用 MyFramework 中的類撵渡、結(jié)構(gòu)體融柬、函數(shù)等了,比如圖中的 doSomething 方法趋距。

Swift5.1 也差不多粒氧,如下圖:

Swift5.1

只是將 swiftmodule 文件改成了 swiftinterface 文件。當(dāng)然 swiftinterface 對(duì)于庫開發(fā)者來說應(yīng)該是可以編輯的节腐。庫開發(fā)者需要將這個(gè)文件和二進(jìn)制文件提供給第三方外盯。

ABI 和模塊的穩(wěn)定意味者二進(jìn)制庫的穩(wěn)定。

對(duì)于更多關(guān)于 Swift 庫穩(wěn)定相關(guān)的內(nèi)容可以查看本文引用中的鏈接铜跑。

??? 問題來了门怪, 如果第三方用的是 Swift5.1 以前的版本開發(fā)的話,那么庫開發(fā)者(使用 Swift5.1)是否需要源碼呢锅纺?

Swift5.1 的優(yōu)化

  1. 編譯出來的二進(jìn)制更小了掷空,通常是 10% 的減少,如果開啟 ‘Optimize for Size‘ 就是 15% 的減少囤锉。
  2. Swift 和 ObjC 的橋接更快了
    1. NSString 和 String 橋接有15x 的提升
    2. NSDictionary 和 Dictionary 有 1.6x 的提升
    3. 大小比較小的 String 的優(yōu)化

這些對(duì) String 和 Dictionary 的優(yōu)化使得 SwiftNIO 請(qǐng)求處理速度提升了 20%坦弟。

SwiftNIO 是 Swift 官方出的跨平臺(tái)異步事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用框架

Swift 開源相關(guān)

  1. Swift 官方 docker 鏡像
  2. Swift 的 SourcKit (用于代碼補(bǔ)全、高亮官地、重構(gòu)以及符號(hào)跳轉(zhuǎn)到定義等編寫 Swift 代碼相關(guān)的編輯器功能)的壓力測(cè)試(為什么壓力測(cè)試酿傍,用過 Xcode 的都知道 Xcode 的 Swift 編輯器有多爛 ??)
  3. LSP(Language Server Protocol)

LSP 的作用如下所示,簡單的說就是代碼編輯器有很多比如 Xcode 的 Source Editor驱入、VSCode赤炒、Vim 等等,每個(gè)編輯器都要支持各種類型代碼(比如 Java亏较、Python莺褒、Swift 等)編輯相關(guān)的操作(高亮、補(bǔ)全等)雪情。

由于 SourceKit 定義的內(nèi)容被 Xcode 使用遵岩,不夠通用,沒法直接給 VSCode 等第三方編輯器使用巡通,所以需要使用 LSP 協(xié)議來做橋接(計(jì)算機(jī)領(lǐng)域有句名言尘执,所有復(fù)雜問題都可以引入另一個(gè)中間層來解決)。
LSP

Swift 新特性

所有的新特性都可以在 Swift Evolution 中找到宴凉。

這里介紹比較重要的一些內(nèi)容誊锭。

單行表達(dá)式隱式返回

這個(gè)很簡單。在以前需要寫這種代碼:

// Implicit return from single expressions
// Swift Evolution: SE-0255
struct Rectangle {
 var width = 0.0, height = 0.0
 var area: Double { return width * height }.
}.

現(xiàn)在 Swift5.1 可以這樣寫了

// Implicit return from single expressions
// Swift Evolution: SE-0255
struct Rectangle {
 var width = 0.0, height = 0.0
 var area: Double { width * height }.
}.

結(jié)構(gòu)體初始化

以前需要這樣初始化結(jié)構(gòu)體

// Synthesized default values for the memberwise initializer
// Proposal and implementation by an open source contributor Alejandro Alonso
// Swift Evolution: SE-0242
struct Dog {
 var name = "Generic dog name"
 var age = 0
}
let boltNewborn = Dog()
let daisyNewborn = Dog(name: "Daisy", age: 0) 
let benjiNewborn = Dog(name: "Benji") // 編譯錯(cuò)誤?

現(xiàn)在最后一句也能編譯通過了弥锄。

字符串插值

字符串插值指的是:

let quantity = 10
label.text = "You have \(quantity) apples"

Swift 5.1 以前炉旷,不能這樣寫

let quantity = 10
label.text = NSLocalizedString(
 "You have \(quantity) apples"
,
 comment: "Number of apples"
) // 編譯錯(cuò)誤?

需要改成這樣子:

let quantity = 10
let formatString = NSLocalizedString(
 "You have %lld apples",
 comment: "Number of apples"
)
label.text = String(format: formatString, quantity)

Swift5.1 可以這樣寫了:

let quantity = 10
return Text(
 "You have \(quantity) apples"
,
 comment: "Number of apples"
)

其中 Text 來自 SwiftUI 框架签孔,定義如下:

// In SwiftUI.framework
public struct Text {
 public init(
 _ key: LocalizedStringKey,
 tableName: String? = nil,
 bundle: Bundle? = nil,
 comment: StaticString? = nil
 )
}

Swift 會(huì)將上述代碼轉(zhuǎn)換成這樣:

let quantity = 10
return Text(
 "You have \(quantity) apples",
 comment: "Number of apples"
)

// Generated by the Swift compiler
var builder = LocalizedStringKey.StringInterpolation(
 literalCapacity: 16, interpolationCount: 1
).
builder.appendLiteral("You have ")
builder.appendInterpolation(quantity)
builder.appendLiteral(" apples")
LocalizedStringKey(stringInterpolation: builder)

這些得益于 ExpressibleByStringInterpolation 這個(gè)新的協(xié)議

返回值類型抽象化

為什么要抽象化返回值類型呢?因?yàn)椴幌氡┞秾?shí)現(xiàn)細(xì)節(jié)窘行,但是在 Swift5.1 以前某些情況下有問題饥追。

如下類型:

// Shapes Example
protocol Shape { /* ... */ }
struct Square: Shape { /* ... */ }
struct Circle: Shape { /* ... */ }
struct Oval: Shape { /* ... */ }
struct Union<A: Shape, B: Shape>: Shape { /* ... */ }
struct Transformed<S: Shape >: Shape { /* ... */ } 

下面這種情況還好:

// API returns different types conforming to the same protocol
// Use Protocol
struct FaceShape {
 …
 var shape: Shape {
   switch faceType {
     case .round:
       return Circle()
     case .square:
       return Square()
     case .diamond:
       return Transformed(Square(), by: .fortyFiveDegrees))
     default:
       return Oval()
     }
   }
}

這種情況:

// API returns the same type but is leaking implementation details
struct EightPointedStar {
 …
 var shape: Union<Square, Transformed<Square>> {
   return Union(Square(), Transformed(Square(), by: .fortyFiveDegrees))
   }
}

就不能寫成下面這樣了:

// API returns the same type but is leaking implementation details
// Protocol type?
struct EightPointedStar {
 …
   var shape: Shape {
     return Union(Square(), Transformed(Square(), by: .fortyFiveDegrees))
   }
}

為什么呢?因?yàn)槿鄙倭祟愋托畔⒐蘅6?Swift 的泛型系統(tǒng)支持的不夠好但绕,同時(shí)阻礙了 Swift 編譯器的優(yōu)化。

在 Swift5.1 中可以這樣寫了(引入了 some 關(guān)鍵字)

// API returns the same type but is leaking implementation details
// Use Opaque Result Types
struct EightPointedStar {
 …
 var shape: some Shape {
   return Union(Square(), Transformed(Square(), by: .fortyFiveDegrees))
 }
}

但是還是不能這樣寫:

// Opaque Result Types
// Compiler enforces that the same type is returned from the implementation
struct EightPointedStar {
 …
 var shape: some Shape {
   if symmetrical {
     return Union(Square(), Transformed(Square(), by: .fortyFiveDegrees))
   } else {
     return Transformed(Square(), by: .twentyDegrees)
   }
 }
}

屬性包裝

很重要;炭础D笏场!

直接上代碼纬黎,在以前幅骄,會(huì)有這樣的代碼:

static var usesTouchID: Bool {
   get {
     return UserDefaults.standard.bool(forKey: "USES_TOUCH_ID")
   }
   set {
     UserDefaults.standard.set(newValue, forKey: "USES_TOUCH_ID")
   }
}

static var isLoggedIn: Bool {
   get {
     return UserDefaults.standard.bool(forKey: "LOGGED_IN")
   }
   set {
     UserDefaults.standard.set(newValue, forKey: "LOGGED_IN")
   }
}

現(xiàn)在,使用 Swift5.1 可以這樣了:

// Using UserDefault property wrapper to declare and access properties
@UserDefault("USES_TOUCH_ID", defaultValue: false)
static var usesTouchID: Bool
@UserDefault("LOGGED_IN", defaultValue: false)
static var isLoggedIn: Bool

if !isLoggedIn && usesTouchID {
 !authenticateWithTouchID()
}

問題來了:@UserDefault 哪里來的本今?UserDefault 其實(shí)是一個(gè)泛型結(jié)構(gòu)體拆座,如下所示:

// The purpose of property wrappers is to wrap a property, specify its access patterns
@propertyWrapper
struct UserDefault<T> {
 let key: String
 let defaultValue: T
 init(_ key: String, defaultValue: T) {
 …
   UserDefaults.standard.register(defaults: [key: defaultValue])
 }

 var value: T {
   get {
     return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
   }
   set {
     UserDefaults.standard.set(newValue, forKey: key)
   }
 }
}

定義以上內(nèi)容之后,我們就可以使用 @UserDefault 這種方式了冠息。

熟悉 Java 的人會(huì)說這是注解挪凑,熟悉 Python 或者 JavaScript 的會(huì)說這是裝飾器,不管如何逛艰,兩者有異曲同工的效果躏碳!

我以前寫過一篇文章介紹這個(gè),感興趣的可以去看看:Swift 中類似 Java 的注解:Attribute

DSL(Domain Specific Languages散怖,領(lǐng)域特定語言)

視頻中 Swift 開發(fā)者介紹了 Swift 就有了一項(xiàng)新的能力: 很容易就能支持 DSL菇绵!

DSL 是一種描述語言,描述一種特定的內(nèi)容镇眷,比如界面的結(jié)構(gòu)等等咬最。這種描述語言需要編程語言作為載體(或者其他,但是編程語言是其中最好的一個(gè)載體)偏灿。對(duì) DSL 不熟悉的可以查看文末詳細(xì)鏈接丹诀。

DSL 簡單的比喻就是钝的,將原本字符串描述的內(nèi)容用編程語言描述出來翁垂。比如 XML 這種格式的字符串可以描述很多,比如界面和數(shù)據(jù)結(jié)構(gòu)等硝桩,但是為什么一定要編程語言描述出來呢沿猜?

  1. 因?yàn)榫幊陶Z言有編譯器的幫助可以幫助你寫出合法、有效的描述碗脊,并且支持編譯優(yōu)化啼肩,
  2. 編程語言可以被執(zhí)行,所以if-else 語句,while祈坠、for 語句害碾,等等各種表達(dá)式以及對(duì)應(yīng)編程語言的所有特性都能使用,幫助你構(gòu)建靈活的描述
  3. 編輯器在編譯器的幫助下可以支持代碼高亮赦拘、補(bǔ)全等等慌随,加快你的開發(fā)。
  4. 編程語言是嚴(yán)謹(jǐn)?shù)木_的躺同,而且開發(fā)人員喜歡寫代碼阁猜!而不是寫各類配置或者字符串描述,想象一下蹋艺,你寫字會(huì)有錯(cuò)別字這種情況剃袍。

接下來介紹 Swift 具體是如何支持 DSL 的。

我們現(xiàn)在可以這樣子用 Swift5.1 寫代碼描述一個(gè) HTML 文檔了:

html {
 head {
   title("\(name)'s WWDC19 Blog")
 }
 body {
   h2 { "Welcome to \(name)'s WWDC19 Blog!" }

   br()

   "Check out the talk schedule and latest news from "

   a {
   "the source"
   }.href("https://developer.apple.com/wwdc19/")

   if !loggedIn {
     button {
       "Log in for comments"
     }
     .onclick("triggerLogin()")
   }
 }..
}..

如何實(shí)現(xiàn)的呢捎谨?

如下所示:

// Functions that construct the HTML objects
public func html(@HTMLBuilder content: () -> HTML) -> HTML { … }
public func head(@HTMLBuilder content: () -> HTML) -> HTML { … }
public func body(@HTMLBuilder content: () -> HTML) -> HTML { … }

可見民效,這些函數(shù)定義使用了上一節(jié)說的屬性包裝器,也就是 HTMLBuilder侍芝,有了這個(gè)包裝器研铆,下面的代碼:

head {
 meta().charset("UTF-8")
 if cond {
   title("Title 1")
 } else {
   title("Title 2")
 }
}

在運(yùn)行時(shí)候的效果類型下面這樣子??:

head {
 let a: HTML = meta().charset("UTF-8")
 let d: HTML
 if cond {
   let b: HTML = title("Title 1")
   d = HTMLBuilder.buildEither(first: b)
 } else {
   let c: HTML = title("Title 2")
   d = HTMLBuilder.buildEither(second: c)
 }
 return HTMLBuilder.buildBlock(a, d)
}

嗯,如果關(guān)注 WWDC19 的話你會(huì)發(fā)現(xiàn)州叠,iOS13 引入的新的 UI 編程框架 SwiftUI.framework 這個(gè)框架就是像上面這樣被實(shí)現(xiàn)的?煤臁!

不過可惜的是 Swift 支持的 @_functionBuilder 在會(huì)上并沒有透露出來咧栗,這個(gè)東西也是筆者在網(wǎng)上找到的逆甜,可以查看本文引用中的 Swift functionBuilder

引用

  1. Apple 介紹 dyld3
  2. Swift 官方博客關(guān)于 ABI 穩(wěn)定的介紹
  3. Swift UTF8 String
  4. SwiftNIO
  5. Swift SourcKit 壓力測(cè)試
  6. LSP
  7. sourcekit-lsp
  8. swift-evolution/
  9. ExpressibleByStringInterpolation
  10. Opaque Result Types
  11. New Design for String Interpolation
  12. SIMD — A Better API for Vector Programming
  13. Property Wrapper Types
  14. Domain Specific Languages
  15. Swift 中類似 Java 的注解:Attribute
  16. Swift functionBuilder
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市致板,隨后出現(xiàn)的幾起案子交煞,更是在濱河造成了極大的恐慌,老刑警劉巖斟或,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件素征,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡萝挤,警方通過查閱死者的電腦和手機(jī)御毅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來怜珍,“玉大人端蛆,你說我怎么就攤上這事∷址海” “怎么了今豆?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵嫌拣,是天一觀的道長。 經(jīng)常有香客問我呆躲,道長异逐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任插掂,我火速辦了婚禮应役,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘燥筷。我一直安慰自己箩祥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布肆氓。 她就那樣靜靜地躺著袍祖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谢揪。 梳的紋絲不亂的頭發(fā)上蕉陋,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音拨扶,去河邊找鬼凳鬓。 笑死,一個(gè)胖子當(dāng)著我的面吹牛患民,可吹牛的內(nèi)容都是我干的缩举。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼匹颤,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼仅孩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起印蓖,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤辽慕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后赦肃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體溅蛉,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年他宛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了船侧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡堕汞,死狀恐怖勺爱,靈堂內(nèi)的尸體忽然破棺而出晃琳,到底是詐尸還是另有隱情讯检,我是刑警寧澤琐鲁,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站人灼,受9級(jí)特大地震影響围段,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜投放,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一奈泪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧灸芳,春花似錦涝桅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谒获,卻和暖如春蛤肌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背批狱。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國打工裸准, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赔硫。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓炒俱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親爪膊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子向胡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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