[譯]七個(gè)Swift中的陷阱以及如何去避免它們

文章總結(jié)翻譯自:Seven Swift Snares & How to Avoid Them

Swift正在完成一個(gè)驚人的壯舉,它正在改變我們?cè)谔O果設(shè)備上編程的方式画舌,引入了很多現(xiàn)代范例,例如:函數(shù)式編程和相比于OC這種純面向?qū)ο笳Z言更豐富的類型檢查已慢。

Swift語言希望通過采用安全的編程模式去幫助開發(fā)者避免bug曲聂。然而這也會(huì)不可避免的產(chǎn)生一些人造的陷阱,他們會(huì)在編譯器不報(bào)錯(cuò)的情況下引入一些Bug佑惠。這些陷阱有的已經(jīng)在Swift book中提到朋腋,有一些還沒有。這里有七個(gè)我在去年遇到的陷阱膜楷,它們涉及Swift協(xié)議擴(kuò)展乍丈、可選鏈和函數(shù)式編程。

協(xié)議擴(kuò)展:強(qiáng)大但是需要謹(jǐn)慎使用

一個(gè)Swift類可以去繼承另一個(gè)類把将,這種能力是強(qiáng)大的。繼承將使類之間的特定關(guān)系更加清晰忆矛,并且支持細(xì)粒度代碼分享察蹲。但是请垛,Swift中如果不是引用類型的話(如:結(jié)構(gòu)體、枚舉)洽议,就不能具有繼承關(guān)系宗收。然而,一個(gè)值類型可以繼承協(xié)議亚兄,同時(shí)協(xié)議可以繼承另一個(gè)協(xié)議混稽。雖然協(xié)議除了類型信息外不能包含其他代碼,但是協(xié)議擴(kuò)展(protocol extension)可以包含代碼审胚。照這種方式匈勋,我們可以用繼承樹來實(shí)現(xiàn)代碼的分享共用,樹的葉子是值類型(結(jié)構(gòu)體或枚舉類)膳叨,樹的內(nèi)部和根是協(xié)議和與他們對(duì)應(yīng)的擴(kuò)展洽洁。

但是Swift協(xié)議擴(kuò)展的實(shí)現(xiàn)依然是一片新的、未開發(fā)的領(lǐng)域菲嘴,尚存在一些問題饿自。代碼并不總是按照我們期望的那樣執(zhí)行。因?yàn)檫@些問題出現(xiàn)在值類型(結(jié)構(gòu)體與枚舉)與協(xié)議組合使用的場景下龄坪,我們將使用類與協(xié)議組合使用的例子去說明這種場景下不存在陷阱昭雌。當(dāng)我們重新改為使用值類型和協(xié)議的時(shí)候?qū)?huì)發(fā)生令人驚奇的事。

開始介紹我們的例子:classy pizza

假設(shè)這里有使用兩種不同谷物制作的三種Pizza

enum Grain  { case Wheat, Corn }

class  NewYorkPizza  { let crustGrain: Grain = .Wheat }
class  ChicagoPizza  { let crustGrain: Grain = .Wheat }
class CornmealPizza  { let crustGrain: Grain = .Corn  }

我們可以通過crustGrain屬性取得披薩所對(duì)應(yīng)的原料

NewYorkPizza().crustGrain   // returns Wheat
ChicagoPizza().crustGrain   // returns Wheat
CornmealPizza().crustGrain  // returns Corn

因?yàn)榇蠖鄶?shù)的Pizza是用小麥(wheat)做的健田,這些公共代碼可以放進(jìn)一個(gè)超類中作為默認(rèn)執(zhí)行的代碼烛卧。

enum Grain { case Wheat, Corn }

class Pizza {
    var crustGrain: Grain { return .Wheat }
    // other common pizza behavior
}
class NewYorkPizza: Pizza {}
class ChicagoPizza: Pizza {}

這些默認(rèn)的代碼可以被重載去處理其它的情況(用玉米制作)

class CornmealPizza: Pizza {
    override var crustGain: Grain { return .Corn }
}

哎呀!這代碼是錯(cuò)的抄课,并且很幸運(yùn)的是編譯器發(fā)現(xiàn)了這些錯(cuò)誤唱星。你能發(fā)現(xiàn)這個(gè)錯(cuò)誤么?我們?cè)诘诙€(gè)crustGain中少寫了r跟磨。Swift通過顯式的標(biāo)注override避免這種錯(cuò)誤间聊。比如在這個(gè)例子中,我們用到了override抵拘,但是拼寫錯(cuò)誤的"crustGain"其實(shí)并沒有重寫任何屬性哎榴,下面是修改后的代碼:

class CornmealPizza: Pizza {
     override var crustGrain: Grain { return .Corn }
}

現(xiàn)在它可以通過編譯并成功運(yùn)行:

NewYorkPizza().crustGrain       // returns Wheat
ChicagoPizza().crustGrain       // returns Wheat
CornmealPizza().crustGrain      // returns Corn

同時(shí)Pizza超類允許我們的代碼在不知道Pizza具體類型的時(shí)候去操作pizzas。我們可以聲明一個(gè)Pizza類型的變量僵蛛。

var pie: Pizza

但是通用類型Pizza仍然可以去得到特定類型的信息尚蝌。

pie =  NewYorkPizza();      pie.crustGrain   // returns Wheat
pie =  ChicagoPizza();      pie.crustGrain   // returns Wheat
pie = CornmealPizza();      pie.crustGrain   // returns Corn

Swift的引用類型在這個(gè)Demo中工作的很好。但是如果這個(gè)程序涉及到并發(fā)性充尉、競爭條件飘言,我們可以使用值類型來避免這些。讓我們來試一下值類型的Pizza吧驼侠!

這里和上面一樣簡單姿鸿,只需要把class修改為struct即可:

enum Grain { case Wheat, Corn }

struct  NewYorkPizza    { let crustGrain: Grain = .Wheat }
struct  ChicagoPizza    { let crustGrain: Grain = .Wheat }
struct CornmealPizza    { let crustGrain: Grain = .Corn  }

執(zhí)行

NewYorkPizza()  .crustGrain     // returns Wheat
ChicagoPizza()  .crustGrain     // returns Wheat
CornmealPizza() .crustGrain     // returns Corn

當(dāng)我們使用引用類型的時(shí)候谆吴,我們通過一個(gè)超類Pizza來達(dá)到目的。但是對(duì)于值類型將要求一個(gè)協(xié)議和一個(gè)協(xié)議擴(kuò)展來合作完成苛预。

protocol Pizza {}

extension Pizza {  var crustGrain: Grain { return .Wheat }  }

struct  NewYorkPizza: Pizza { }
struct  ChicagoPizza: Pizza { }
struct CornmealPizza: Pizza {  let crustGain: Grain = .Corn }

這段代碼可以通過編譯句狼,我們來測試一下:

NewYorkPizza().crustGrain       // returns Wheat
 ChicagoPizza().crustGrain      // returns Wheat
CornmealPizza().crustGrain      // returns Wheat  What?!

對(duì)于執(zhí)行結(jié)果,我們想說cornmeal pizza并不是Wheat制作的热某,返回結(jié)果出現(xiàn)錯(cuò)誤腻菇!哎呀!我把
struct CornmealPizza: Pizza { let crustGain: Grain = .Corn }
中的 crustGrain寫成了crustGain昔馋,再一次忘記了r筹吐,但是對(duì)于值類型這里沒有override關(guān)鍵字去幫助編譯器去發(fā)現(xiàn)我們的錯(cuò)誤。沒有編譯器的幫助绒极,我們不得不更加小心的編寫代碼骏令。

?? 在協(xié)議擴(kuò)展中重寫協(xié)議中的屬性時(shí)要仔細(xì)核對(duì)

ok,我們把這個(gè)拼寫錯(cuò)誤改正過來:

struct CornmealPizza: Pizza {  let crustGrain: Grain = .Corn }

重新執(zhí)行

 NewYorkPizza().crustGrain      // returns Wheat
 ChicagoPizza().crustGrain      // returns Wheat
 CornmealPizza().crustGrain     // returns Corn  Hooray!    

為了在討論Pizza的時(shí)候不需要擔(dān)心到底是New York, Chicago, 還是 cornmeal垄提,我們可以使用Pizza協(xié)議作為變量的類型榔袋。

var pie: Pizza

這個(gè)變量能夠在不同種類的Pizza中去使用

pie =  NewYorkPizza(); pie.crustGrain  // returns Wheat
pie =  ChicagoPizza(); pie.crustGrain  // returns Wheat
pie = CornmealPizza(); pie.crustGrain  // returns Wheat    Not again?!

為什么這個(gè)程序顯示cornmeal pizza 包含wheat?Swift編譯代碼的時(shí)候忽略了變量的目前實(shí)際值铡俐。代碼只能夠使用編譯時(shí)期的知道的信息凰兑,并不知道運(yùn)行時(shí)期的具體信息。程序中可以在編譯時(shí)期得到的信息是piepizza類型审丘,pizza協(xié)議擴(kuò)展返回wheat吏够,所以在結(jié)構(gòu)體CornmealPizza中的重寫起不到任何作用。雖然編譯器本能夠在使用靜態(tài)調(diào)度替換動(dòng)態(tài)調(diào)度時(shí)滩报,為潛在的錯(cuò)誤提出警告锅知,但它實(shí)際上并沒有這么做。這里的粗心將帶來巨大的陷阱脓钾。

在這種情況下售睹,Swift提供一種解決方案,除了在協(xié)議擴(kuò)展中(extension)定義crustGrain屬性之外可训,還可以在協(xié)議中聲明昌妹。

protocol  Pizza {  var crustGrain: Grain { get }  }
extension Pizza {  var crustGrain: Grain { return .Wheat }  }

在協(xié)議內(nèi)聲明變量并在協(xié)議拓展中定義,這樣會(huì)告訴編譯器關(guān)注變量pie運(yùn)行時(shí)的值握截。

在協(xié)議中一個(gè)屬性的聲明有兩種不同的含義飞崖,靜態(tài)還是動(dòng)態(tài)調(diào)度,取決于是否這個(gè)屬性在協(xié)議擴(kuò)展中定義谨胞。

補(bǔ)充了協(xié)議中變量的聲明后固歪,代碼可以正常運(yùn)行了:

pie =  NewYorkPizza();  pie.crustGrain   // returns Wheat
pie =  ChicagoPizza();  pie.crustGrain   // returns Wheat
pie = CornmealPizza();  pie.crustGrain   // returns Corn    Whew!

?? 在協(xié)議擴(kuò)展中定義的每一個(gè)屬性,需要在協(xié)議中進(jìn)行聲明

然而這個(gè)設(shè)法避免陷阱的方式并不總是有效的胯努。

導(dǎo)入的協(xié)議不能夠完全擴(kuò)展昼牛。

框架(庫)可以使一個(gè)程序?qū)虢涌谌ナ褂檬跷停槐匕嚓P(guān)實(shí)現(xiàn)。例如蘋果提供給我們提供了需要框架贰健,實(shí)現(xiàn)了用戶體驗(yàn)、系統(tǒng)設(shè)施和其他功能恬汁。Swift的擴(kuò)展允許程序向?qū)氲念惲娲弧⒔Y(jié)構(gòu)體、枚舉和協(xié)議中添加自己的屬性(這里的屬性并不是存儲(chǔ)屬性)氓侧。通過協(xié)議拓展添加的屬性脊另,就好像它原來就在協(xié)議中一樣。但實(shí)際上定義在協(xié)議拓展中的屬性并非一等公民约巷,因?yàn)橥ㄟ^協(xié)議拓展無法添加屬性的聲明偎痛。

我們首先實(shí)現(xiàn)一個(gè)框架,這個(gè)框架定義了Pizza協(xié)議和具體的類型

// PizzaFramework:

public protocol Pizza { }

public struct  NewYorkPizza: Pizza  { public init() {} }
public struct  ChicagoPizza: Pizza  { public init() {} }
public struct CornmealPizza: Pizza  { public init() {} }

導(dǎo)入框架并且擴(kuò)展Pizza

import PizzaFramework

public enum Grain { case Wheat, Corn }

extension Pizza         { var crustGrain: Grain { return .Wheat } }
extension CornmealPizza { var crustGrain: Grain { return .Corn  } }

和以前一樣独郎,靜態(tài)調(diào)度產(chǎn)生一個(gè)錯(cuò)誤的答案

var pie: Pizza = CornmealPizza()
pie.crustGrain                            // returns Wheat   Wrong!

這個(gè)是因?yàn)椋ㄅc剛才的解釋一樣)這個(gè)crustGrain屬性并沒有在協(xié)議中聲明踩麦,而是只是在擴(kuò)展中定義。然而氓癌,我們沒有辦法對(duì)框架的代碼進(jìn)行修改谓谦,因此也就不能解決這個(gè)問題。因此贪婉,想要通過擴(kuò)展增加其他框架的協(xié)議屬性是不安全的反粥。

?? 不要對(duì)導(dǎo)入的協(xié)議進(jìn)行擴(kuò)展,新增可能需要?jiǎng)討B(tài)調(diào)度的屬性

正像剛才描述的那樣疲迂,框架與協(xié)議擴(kuò)展之間的交互才顿,限制了協(xié)議擴(kuò)展的效用,但是框架并不是唯一的限制因素尤蒿,同樣郑气,類型約束也不利于協(xié)議擴(kuò)展。

Attributes in restricted protocol extensions: declaration is no longer enough

回顧一下此前Pizza的例子:

enum Grain { case Wheat, Corn }

protocol  Pizza { var crustGrain: Grain { get }  }
extension Pizza { var crustGrain: Grain { return .Wheat }  }

struct  NewYorkPizza: Pizza  { }
struct  ChicagoPizza: Pizza  { }
struct CornmealPizza: Pizza  { let crustGrain: Grain = .Corn }

讓我們用Pizza做一頓飯优质。不幸的是竣贪,并不是每頓飯都會(huì)吃pizza,所以我們使用一個(gè)通用的Meal結(jié)構(gòu)體來適應(yīng)各種情況巩螃。我們只需要傳入一個(gè)參數(shù)就可以確定進(jìn)餐的具體類型演怎。

struct Meal: MealProtocol {
    let mainDish: MainDishOfMeal
}

結(jié)構(gòu)體Meal繼承自MealProtocol協(xié)議,它可以測試meal是否包含谷蛋白避乏。

protocol MealProtocol {
    typealias MainDish_OfMealProtocol
    var mainDish: MainDish_OfMealProtocol {get}
    var isGlutenFree: Bool {get}
}

為了避免中毒爷耀,代碼中使用了默認(rèn)值(不含有谷蛋白)

extension MealProtocol {
    var isGlutenFree: Bool  { return false }
}

Swift中的 Where提供了一種方式去表達(dá)約束性協(xié)議擴(kuò)展。當(dāng)主菜是pizza的時(shí)候拍皮,我們知道pizzascrustGrain屬性歹叮,我們就可以訪問這個(gè)屬性跑杭。如果沒where這里的限制,我們?cè)诓皇?code>Pizza的情況下訪問scrustGrain是不安全的咆耿。

extension MealProtocol  where  MainDish_OfMealProtocol: Pizza {
    var isGlutenFree: Bool  { return mainDish.crustGrain == .Corn }
}

一個(gè)帶有Where的擴(kuò)展叫做約束性擴(kuò)展德谅。

讓我們做一份美味的cornmeal Pizza

let meal: Meal = Meal(mainDish: CornmealPizza())

結(jié)果:

meal.isGlutenFree   // returns false
// 根據(jù)協(xié)議拓展,理論上應(yīng)該返回true   

正像我們?cè)谇懊嫘」?jié)演示的那樣萨螺,當(dāng)發(fā)生動(dòng)態(tài)調(diào)度的時(shí)候窄做,我們應(yīng)該在協(xié)議中聲明,并且在協(xié)議擴(kuò)展中進(jìn)行定義慰技。但是約束性擴(kuò)展的定義總是靜態(tài)調(diào)度的椭盏。為了防止由于意外的靜態(tài)調(diào)度而引起的bug:

?? 如果一個(gè)新的屬性需要?jiǎng)討B(tài)調(diào)度,避免使用約束性協(xié)議擴(kuò)展

使用可選鏈賦值和副作用

Swift可以通過靜態(tài)地檢查變量是否為nil來避免錯(cuò)誤吻商,并使用一種方便的縮略表達(dá)式掏颊,可選鏈,用于忽略可能出現(xiàn)的nil艾帐。這一點(diǎn)也正是Objective-C的默認(rèn)行為乌叶。

不幸的是,如果可選鏈中被賦值的引用有可能為空掩蛤,就可能導(dǎo)致錯(cuò)誤枉昏,考慮下面這段代碼,Holder中存放一個(gè)整數(shù):

class Holder  {
    var x = 0
}

var n = 1
var h: Holder? = nil
h?.x = n++

在這段代碼的最后一行中揍鸟,我們把n++賦值給h的屬性兄裂。除了賦值以外,變量n還會(huì)自增阳藻,我們稱此為副作用晰奖。

變量n最終的值會(huì)取決于h是否為nil。如果h不為nil腥泥,那么賦值語句執(zhí)行匾南,n++也會(huì)執(zhí)行。但如果h為nil蛔外,不僅賦值語句不會(huì)執(zhí)行蛆楞,n++也不會(huì)執(zhí)行。為了避免沒有發(fā)生副作用導(dǎo)致的令人驚訝的結(jié)果夹厌,我們應(yīng)該:

?? 避免把一個(gè)有副作用的表達(dá)式的結(jié)果通過可選鏈賦值給等號(hào)左邊的變量

函數(shù)編程陷阱

由于Swift的支持豹爹,函數(shù)式編程的優(yōu)點(diǎn)得以被帶入蘋果的生態(tài)圈中。Swift中的函數(shù)和閉包都是一等公民矛纹,不僅方便易用而且功能強(qiáng)大臂聋。不幸的是,其中也有一些我們需要小心避免的陷阱。

比如孩等,inout參數(shù)會(huì)在閉包中默默的失效艾君。

Swift的inout參數(shù)允許函數(shù)接受一個(gè)參數(shù)并直接對(duì)參數(shù)賦值,Swift的閉包支持在執(zhí)行過程中引用被捕獲的函數(shù)肄方。這些特性有助于我們寫出優(yōu)雅易讀的代碼冰垄,所以你也許會(huì)把它們結(jié)合起來使用,但這種結(jié)合有可能會(huì)導(dǎo)致問題权她。

我們重寫crustGrain屬性來說明inout參數(shù)的使用播演,為簡單起見,開始時(shí)先不使用閉包:

enum Grain {
    case Wheat, Corn
}

struct CornmealPizza {
    func setCrustGrain(inout grain: Grain)  {
        grain = .Corn
    }
}

為了測試這個(gè)函數(shù)伴奥,我們給它傳一個(gè)變量作為參數(shù)。函數(shù)返回后翼闽,這個(gè)變量的值應(yīng)該從Wheat變成了Corn:

let pizza = CornmealPizza()
var grain: Grain = .Wheat
pizza.setCrustGrain(&grain)
grain       // returns Corn

現(xiàn)在我們嘗試在函數(shù)中返回閉包拾徙,然后在閉包中設(shè)置參數(shù)的值:

struct CornmealPizza {
    func getCrustGrainSetter() -> (inout grain: Grain) -> Void {
        return { (inout grain: Grain) in
            grain = .Corn
        }
    }
}

使用這個(gè)閉包只需要多一次調(diào)用:

var grain: Grain = .Wheat
let pizza = CornmealPizza()
let aClosure = pizza.getCrustGrainSetter()
grain           // returns Wheat (We have not run the closure yet)
aClosure(grain: &grain)
grain           // returns Corn

到目前為止一切正常,但如果我們直接把參數(shù)傳進(jìn)getCrustGrainSetter函數(shù)而不是閉包呢感局?

struct CornmealPizza {
    func getCrustGrainSetter(inout grain: Grain)  ->  () -> Void {
        return { grain = .Corn }
    }
}

然后再試一次:

var grain: Grain = .Wheat
let pizza = CornmealPizza()
let aClosure = pizza.getCrustGrainSetter(&grain)
print(grain)                // returns Wheat (We have not run the closure yet)
aClosure()
print(grain)                // returns Wheat  What?!?

inout參數(shù)在傳入閉包的作用域外時(shí)會(huì)失效尼啡,所以:

?? 避免在閉包中使用inout參數(shù)

這個(gè)問題在Swift文檔中提到過,但還有一個(gè)與之相關(guān)的問題值得注意询微,這與創(chuàng)建的閉包的等價(jià)方法:柯里化有關(guān)崖瞭。

在使用柯里化技術(shù)時(shí),inout參數(shù)顯得前后矛盾撑毛。

在一個(gè)創(chuàng)建并返回閉包的函數(shù)中书聚,Swift為函數(shù)的類型和主體提供了一種簡潔的語法。盡管這種柯里化看上去僅是一種縮略表達(dá)式藻雌,但它與inout參數(shù)結(jié)合使用時(shí)卻會(huì)給人們帶來一些驚訝雌续。為了說明這一點(diǎn),我們用柯里化語法實(shí)現(xiàn)上面那個(gè)例子胯杭。函數(shù)沒有聲明為返回一個(gè)閉包驯杜,而是在第一個(gè)參數(shù)列表后加上了第二個(gè)參數(shù)列表,然后在函數(shù)體內(nèi)省略了顯式的閉包創(chuàng)建:

struct CornmealPizza {
    func getCrustGrainSetterWithCurry(inout grain: Grain)() -> Void {
        grain = .Corn
    }
}

和顯式創(chuàng)建閉包時(shí)一樣做个,我們調(diào)用這個(gè)函數(shù)然后返回一個(gè)閉包:

var grain: Grain = .Wheat
let pizza = CornmealPizza()
let aClosure = pizza.getCrustGrainSetterWithCurry(&grain)

在上面的例子中鸽心,閉包被顯式創(chuàng)建但沒能成功為inout參數(shù)賦值,但這次就成功了:

aClosure()
grain               // returns Corn

這說明在柯里化函數(shù)中居暖,inout參數(shù)可以正常使用顽频,但是顯式的創(chuàng)建閉包時(shí)就不行了。

?? 避免在柯里化函數(shù)中使用inout參數(shù)膝但,因?yàn)槿绻愫髞韺⒖吕锘臑轱@式的創(chuàng)建閉包冲九,這段代碼就會(huì)產(chǎn)生錯(cuò)誤

總結(jié):七個(gè)避免

  • 在協(xié)議擴(kuò)展中重寫協(xié)議中的屬性時(shí)要仔細(xì)核對(duì)
  • 在協(xié)議擴(kuò)展中定義的每一個(gè)屬性,需要在協(xié)議中進(jìn)行聲明
  • 不要對(duì)導(dǎo)入的第三方協(xié)議進(jìn)行屬性擴(kuò)展,那樣可能需要?jiǎng)討B(tài)調(diào)度
  • 如果一個(gè)新的屬性需要?jiǎng)討B(tài)調(diào)度莺奸,避免使用約束性協(xié)議擴(kuò)展
  • 避免把一個(gè)有副作用的表達(dá)式的結(jié)果通過可選鏈賦值給等號(hào)左邊的變量
  • 避免在閉包中使用inout參數(shù)
  • 避免在柯里化函數(shù)中使用inout參數(shù)丑孩,因?yàn)槿绻愫髞韺⒖吕锘臑轱@式的創(chuàng)建閉包,這段代碼就會(huì)產(chǎn)生錯(cuò)誤
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末灭贷,一起剝皮案震驚了整個(gè)濱河市温学,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌甚疟,老刑警劉巖仗岖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異览妖,居然都是意外死亡轧拄,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門讽膏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來檩电,“玉大人,你說我怎么就攤上這事府树±” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵奄侠,是天一觀的道長卓箫。 經(jīng)常有香客問我,道長垄潮,這世上最難降的妖魔是什么烹卒? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮魂挂,結(jié)果婚禮上甫题,老公的妹妹穿的比我還像新娘。我一直安慰自己涂召,他們只是感情好坠非,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著果正,像睡著了一般炎码。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秋泳,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天潦闲,我揣著相機(jī)與錄音,去河邊找鬼迫皱。 笑死歉闰,一個(gè)胖子當(dāng)著我的面吹牛辖众,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播和敬,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼凹炸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了昼弟?” 一聲冷哼從身側(cè)響起啤它,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎舱痘,沒想到半個(gè)月后变骡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芭逝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年塌碌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旬盯。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡誊爹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瓢捉,到底是詐尸還是另有隱情,我是刑警寧澤办成,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布泡态,位于F島的核電站,受9級(jí)特大地震影響迂卢,放射性物質(zhì)發(fā)生泄漏某弦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一而克、第九天 我趴在偏房一處隱蔽的房頂上張望靶壮。 院中可真熱鬧,春花似錦员萍、人聲如沸腾降。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽螃壤。三九已至,卻和暖如春筋帖,著一層夾襖步出監(jiān)牢的瞬間奸晴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國打工日麸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寄啼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像墩划,于是被迫代替她去往敵國和親涕刚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

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

  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young閱讀 3,814評(píng)論 1 10
  • Hello Word 在屏幕上打印“Hello, world”走诞,可以用一行代碼實(shí)現(xiàn): 你不需要為了輸入輸出或者字符...
    restkuan閱讀 3,181評(píng)論 0 6
  • 基礎(chǔ)部分(The Basics) 當(dāng)推斷浮點(diǎn)數(shù)的類型時(shí)副女,Swift 總是會(huì)選擇Double而不是Float。 結(jié)合...
    gamper閱讀 1,293評(píng)論 0 7
  • 不知道有多少人關(guān)注我蚣旱,其實(shí)這個(gè)問題一點(diǎn)也不重要碑幅。正如我剛來簡書的時(shí)候,懷著一種要紅的心情塞绿,現(xiàn)在想來很可笑沟涨。我寫了3...
    沒有雞湯閱讀 768評(píng)論 11 27
  • HTML、XML异吻、XHTML有什么區(qū)別裹赴? HTML (HyperText Markup Language)超文本標(biāo)...
    hui_mamba閱讀 370評(píng)論 0 1