iOS開發(fā) - 老生常談的循環(huán)引用問題

debug-like-a-tuner.jpg

內(nèi)存管理在iOS開發(fā)中很重要鸦采,在iOS 5之前毙驯,開發(fā)者需要使用MRC(Manual Reference Count)來進(jìn)行對(duì)象的內(nèi)存管理竟坛;為了方便開發(fā)者,從iOS 6開始框冀,蘋果引入了ARC(Automatic Reference Count)來進(jìn)行內(nèi)存管理流椒,這無疑大大地減少了開發(fā)者的工作量。

ARC本質(zhì)還是MRC左驾,它是在Xcode編譯期間镣隶,在代碼適當(dāng)?shù)奈恢锰砑?code>retain ,releaseautorelease操作极谊。在絕大多數(shù)時(shí)間诡右,我們可以使用ARC愉快地寫代碼,但是為了寫出更加安全和健壯的代碼轻猖,開發(fā)者還是需要了解內(nèi)存管理的知識(shí)帆吻,以防因?yàn)閮?nèi)存管理不當(dāng)導(dǎo)致莫名其妙的問題。

在swift編碼過程中咙边,大多數(shù)時(shí)候開發(fā)者過于相信ARC猜煮,一不小心還是可能踩到循環(huán)引用的坑里面。并且循環(huán)引用的問題往往會(huì)潛伏起來败许,在開發(fā)的初期王带,并不會(huì)導(dǎo)致異常,但是隨著業(yè)務(wù)復(fù)雜度增加市殷、參與的人增多愕撰,潛在的循環(huán)引用的問題就會(huì)出現(xiàn)。而解決循環(huán)引用導(dǎo)致的問題,也會(huì)消耗不少的人力和時(shí)間搞挣,回顧循環(huán)引用產(chǎn)生的原因带迟,一方面是開發(fā)者的經(jīng)驗(yàn)不足,甚至不知道循環(huán)引用的概念囱桨,所以寫不出高質(zhì)量的代碼仓犬;另一方面,有可能是開發(fā)過程中并沒有引入規(guī)范的編碼方式舍肠,一個(gè)人踩了坑搀继,找到了解決方案,并不能推廣給團(tuán)隊(duì)中的其他成員翠语,導(dǎo)致其他人前赴后繼地踩坑律歼。

本篇文章,主要討論了swift中解決循環(huán)引用的方案啡专,并結(jié)合一個(gè)案例险毁,幫助讀者加深理解。

1. Swift中的循環(huán)引用

在swift中们童,對(duì)象引用時(shí)默認(rèn)是強(qiáng)strong引用畔况,如果兩個(gè)對(duì)象相互持有時(shí),則會(huì)造成循環(huán)引用慧库,為了解決這個(gè)問題跷跪,swift引入了weakunowned兩個(gè)修飾關(guān)鍵字。

相對(duì)于強(qiáng)strong引用齐板,weakunowned則稱為弱引用和無主引用吵瞻,weak和unowned都不會(huì)對(duì)一個(gè)引用對(duì)象產(chǎn)生強(qiáng)引用,這在基本原理上來說是因?yàn)樗鼈兌疾粫?huì)增加對(duì)象的引用計(jì)數(shù)(retain count)甘磨。

按照我們OC時(shí)代的經(jīng)驗(yàn)橡羞,weak就可以解決循環(huán)引用的問題,開發(fā)者會(huì)感到奇怪济舆,swift有了weak卿泽,為什么swift還要引入unowned關(guān)鍵字?以及滋觉,unowned和weak有什么區(qū)別呢签夭?

1.1 weak, unowned產(chǎn)生背景

之所以有weak和unowned兩個(gè)關(guān)鍵字,則是與swift語(yǔ)言的可選(optional type)類型相關(guān)椎侠,關(guān)于可選類型的技術(shù)點(diǎn)不是本篇探討的重點(diǎn)第租,此處不再贅述。

如果我纪,我是說如果慎宾,如果swift語(yǔ)言沒有optional概念儡羔,那么使用weak足以解決循環(huán)引用的問題。現(xiàn)在璧诵,swift為了保證代碼安全性汰蜘,引入了optional概念。說到底之宿,optional是一個(gè)類型族操,它與Int, String或者諸如自定義的Person類一樣;但是optional類型特殊之處在于它可以用來包裹其他的類型比被,例如var person: Person?, let number: Int?就是用optional類型包裹了Person和Int類型色难。這樣說來,swift中定義屬性有兩種形式等缀,即optional和non-optional枷莉,僅僅一個(gè)weak關(guān)鍵字不足以同時(shí)解決optional和non-optional兩種情況,所以swift引入了另一個(gè)關(guān)鍵字unowned尺迂。

1.2 weak與optional相關(guān)

弱(weak)引用允許引用對(duì)象為空笤妙,并且我們知道,在swift中只有optional類型定義的變量或?qū)ο蟛趴梢栽O(shè)置為空噪裕,所以如果我們使用weak關(guān)鍵字定義屬性蹲盘,則必須保證該屬性是optional類型。

1.3 unowned與non-optional相關(guān)

使用unowned時(shí)膳音,我們會(huì)假定一個(gè)對(duì)象a的無主引unowned reference用b在其持有者a的生命周期中永遠(yuǎn)不會(huì)被置為nil召衔,同時(shí)必須保證一個(gè)無主引用對(duì)象unowned reference在它的初始化方法中就被賦值,這也意味著該無主引用對(duì)象應(yīng)該定義為non-optional類型祭陷,這樣我們就可以安全使用該對(duì)象苍凛,而不必須進(jìn)行安全檢查。

如果因?yàn)槟承┰虮荆粋€(gè)無主引用對(duì)象從內(nèi)存中被釋放醇蝴,那么當(dāng)我們使用該對(duì)象的時(shí)候,App就會(huì)閃退毒姨。

1.4 參考Apple官方的建議

上面的內(nèi)容對(duì)于讀者來說哑蔫,可能還是感覺含糊不清,我們來看一下Apple文檔中的建議吧弧呐,概括起來大概就是下面兩條原則,

  1. 當(dāng)一個(gè)對(duì)象有可能在它的生命周期內(nèi)被設(shè)置為nil時(shí)候嵌纲,使用weak關(guān)鍵字俘枫;
  2. 當(dāng)你可以確保一個(gè)對(duì)象在它的生命周期內(nèi)不會(huì)被設(shè)置為nil時(shí),使用unowned關(guān)鍵字逮走。

同時(shí)鸠蚪,文檔中也提供了兩個(gè)demo,探討了循環(huán)引用的案例和打破循環(huán)引用的方法,

先看一下使用weak的demo茅信,如下代碼所示盾舌,

// weak關(guān)鍵字例子

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
}

class Apartment {
    let number: Int
    init(number: Int) { self.number = number }
    weak var person: Person? // 承租人、占用者
}

這個(gè)demo是一個(gè)人和公寓的簡(jiǎn)單模型蘸鲸,Person類有一個(gè)Apartment類型的optional屬性apartment妖谴,同時(shí),Apartment類有一個(gè)Person類型的person(承租人)屬性酌摇,該屬性也是optional類型膝舅。

Optional在此處表明的意思就是Person對(duì)象可能會(huì)持有apartment屬性,也可能不會(huì)窑多;Apartment對(duì)象可能會(huì)仍稀、也可能不會(huì)持有person屬性。這會(huì)造成一個(gè)問題埂息,如果Person和Apartment雙方都持有了對(duì)方技潘,就形成了循環(huán)引用,為了打破循環(huán)引用千康,將Apartment類的Person類型的person屬性改為weak修飾崭篡,經(jīng)過這樣修改之后,兩者的關(guān)系如下圖所示 吧秕,

weak-reference.png

這是一個(gè)很好的demo琉闪,展示了使用weak關(guān)鍵字的場(chǎng)景。使用weak關(guān)鍵字打破了循環(huán)引用之后砸彬,兩個(gè)對(duì)象之間不會(huì)形成緊密的依賴關(guān)系颠毙,能夠在適當(dāng)?shù)臅r(shí)機(jī)從內(nèi)存中釋放。

再看一下使用unowned的demo砂碉,如下代碼蛀蜜,


// unowned關(guān)鍵字例子

class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) { self.name = name }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer
    init(number: UInt64, customer: Customer) { 
        self.number = number
        self.customer = customer
    }
}

在這個(gè)demo中,是消費(fèi)者和信用卡的模型增蹭,一個(gè)消費(fèi)者可能會(huì)擁有滴某、也可能沒有一張信用卡;而一張信用卡必定有一個(gè)關(guān)聯(lián)的消費(fèi)者滋迈。為了用代碼表示兩者之間的關(guān)系霎奢,一個(gè)Customer類有一個(gè)optional類型的creditCard屬性,而一個(gè)CreditCard類型有一個(gè)non-optional類型饼灿、unowned修飾的customer屬性幕侠。他們之間的關(guān)系如下圖所示,

unowned-reference.png

2. 閉包引起的循環(huán)引用

當(dāng)然除了兩個(gè)對(duì)象相互持有造成循環(huán)引用碍彭,還有一種情況也會(huì)造成循環(huán)引用晤硕,那就是閉包(在swift中叫閉包closure悼潭,在OC中叫代碼塊block),并且使用閉包或代碼塊的場(chǎng)景更多舞箍,更容易因?yàn)榫幋a疏忽或不規(guī)范造成潛在的循環(huán)引用舰褪。筆者在開發(fā)過程中就碰到了這種神坑,下面筆者就回顧一下解決bug的過程疏橄,希望能夠拋磚引玉占拍,也希望讀者在開發(fā)過程中能避開這類錯(cuò)誤。

2.1 Bug起因

最近的開發(fā)周软族,同事M在接手另一個(gè)同事K的工作任務(wù)刷喜,他在開發(fā)過程中遇到一個(gè)很奇怪的現(xiàn)象,簡(jiǎn)單描述一下立砸,在HomeVC頁(yè)面上是一些品牌信息列表掖疮,用戶可以點(diǎn)擊進(jìn)入詳情,也可以點(diǎn)擊關(guān)注按鈕直接關(guān)注颗祝;還可以點(diǎn)擊導(dǎo)航欄更多品牌按鈕浊闪,跳轉(zhuǎn)到MoreBrandListVC,這個(gè)列表顯示更多的品牌信息螺戳。如下圖所示搁宾,

follow-brands.png

按照設(shè)計(jì)和編碼原則,HomeVC的品牌關(guān)注狀態(tài)應(yīng)該與MoreBrandListVC對(duì)應(yīng)的品牌關(guān)注狀態(tài)保持一致倔幼,但測(cè)試同事提的比較奇怪的問題就是在HomeVC和MoreBrandListVC之間反復(fù)切換盖腿,并且多次重復(fù)點(diǎn)擊關(guān)注某個(gè)品牌以后,例如關(guān)注了“品牌3”损同,點(diǎn)擊“返回”pop回到HomeVC時(shí)候翩腐,下拉刷新,發(fā)現(xiàn)在HomeVC頁(yè)面膏燃,“品牌3”的關(guān)注狀態(tài)是未關(guān)注茂卦,與MoreBrandListVC關(guān)注狀態(tài)不一致。

剛開始分析該問題時(shí)候组哩,以為是客戶端多次操作等龙,發(fā)送請(qǐng)求太多,服務(wù)端處理不及時(shí)或者服務(wù)端緩存造成的返回結(jié)果不一致伶贰,準(zhǔn)備強(qiáng)行甩鍋給服務(wù)端開發(fā)人員蛛砰。后來服務(wù)端同事說他沒有做緩存,而且android是OK的幕袱,這就尷尬了暴备。

2.2 多人協(xié)作定位bug

既然服務(wù)端同事來幫分析問題了,咱們就把他叫過來一塊看看Xcode的打印日志们豌,按照測(cè)試的操作步驟涯捻,iOS同事M不久就重現(xiàn)了測(cè)試指出的bug,在Xcode的console控制臺(tái)中看了看服務(wù)端返回的數(shù)據(jù)望迎,跟客戶端顯示的是一致的障癌。服務(wù)端同事也有點(diǎn)啞口無言,但是他突然看出了異常的情況辩尊,就是在Xcode的控制臺(tái)有很多發(fā)送“某品牌關(guān)注狀態(tài)更新的請(qǐng)求”的日志涛浙,按照道理,無論是在HomeVC點(diǎn)擊關(guān)注或下拉刷新摄欲,亦或是在MoreBrandListVC點(diǎn)擊關(guān)注品牌轿亮,操作再?gòu)?fù)雜,最多也就十幾條請(qǐng)求吧胸墙,但是我們看到的實(shí)際情況是Xcode打印了四五十條的請(qǐng)求日志我注。這一點(diǎn)很讓人費(fèi)解,我們定位了一下發(fā)起請(qǐng)求的地方迟隅,原來是在MoreBrandListVC里面但骨,更具體地來說,就是MoreBrandListVC觀察了某個(gè)notification智袭,收到notification之后進(jìn)行了網(wǎng)絡(luò)請(qǐng)求奔缠。

問題應(yīng)該就是出現(xiàn)在MoreBrandListVC上面,在回到HomeVC之后吼野,iOS系統(tǒng)會(huì)在合適的時(shí)機(jī)將MoreBrandListVC對(duì)象從內(nèi)存中釋放校哎;而現(xiàn)在出現(xiàn)的問題是,在HomeVC頁(yè)面瞳步,MoreBrandListVC對(duì)象內(nèi)部還在接收到notification之后發(fā)送網(wǎng)絡(luò)請(qǐng)求闷哆。這說明MoreBrandListVC對(duì)象的內(nèi)存并沒有被合理的釋放,而是一直存在于內(nèi)存中谚攒,造成MoreBrandListVC不能釋放的原因極有可能是循環(huán)引用阳准。

請(qǐng)讀者再看一下測(cè)試重現(xiàn)bug的操作,反復(fù)在HomeVC和MoreBrandListVC之間切換馏臭,每一次從HomeVC頁(yè)面push到MoreBrandListVC頁(yè)面野蝇,就創(chuàng)建了一個(gè)MoreBrandListVC對(duì)象,而因?yàn)槟巢糠执a原因括儒,導(dǎo)致MoreBrandListVC對(duì)象與另一個(gè)對(duì)象形成了循環(huán)引用绕沈。這樣,內(nèi)存中就存在了多個(gè)MoreBrandListVC對(duì)象帮寻,這多個(gè)對(duì)象再接收到notification之后乍狐,就開始進(jìn)行網(wǎng)絡(luò)請(qǐng)求,這就是Xcode日志中看到四五十條網(wǎng)絡(luò)請(qǐng)求的原因固逗。

找到這樣一個(gè)bug浅蚪,也是一個(gè)挺艱難和復(fù)雜的過程藕帜,簡(jiǎn)單描述一下,重現(xiàn)并定位該bug惜傲,大概是這樣幾個(gè)步驟洽故,

  1. 測(cè)試部門同事反復(fù)、多次的非常規(guī)操作盗誊,以及他堅(jiān)持不懈追究到底的決心时甚;
  2. 服務(wù)端和客戶端開發(fā)一起看Xcode打印日志,并由服務(wù)端同事發(fā)現(xiàn)過多請(qǐng)求的異常哈踱;
  3. 客戶端同事分析現(xiàn)象荒适,定位bug原因是循環(huán)引用。

以上开镣,只是簡(jiǎn)單概括找出bug的過程刀诬,實(shí)際上中間的坑更多。最麻煩的一點(diǎn)大概就是這一塊的bug是之前的版本就已經(jīng)存在了哑子,只是當(dāng)時(shí)沒有測(cè)試出來舅列,而現(xiàn)在不知道隔了多久,萬年老坑被挖出來卧蜓,已經(jīng)不知道當(dāng)時(shí)這塊代碼的維護(hù)者是哪個(gè)人帐要。可以說弥奸,bug時(shí)間越久榨惠,就越難排查。

2.3 逐步排查盛霎,精確打擊

在分析可能造成循環(huán)引用的地方赠橙,我們進(jìn)行了一一排查,大體思路是這樣的愤炸,

  • 首先期揪,MoreBrandListVC的對(duì)象創(chuàng)建是在HomeVC內(nèi),那么MoreBrandListVC對(duì)象有沒有反向的持有HomeVC呢规个,仔細(xì)看了看代碼凤薛,并沒有這種情況;
  • 其次诞仓,有沒有可能是MoreBrandListVC內(nèi)部與其他block相互持有造成了循環(huán)引用呢缤苫。我在MoreBrandListVC文件里面搜索了block關(guān)鍵字,搜索到了4個(gè)墅拭,其中有兩個(gè)是網(wǎng)絡(luò)請(qǐng)求的block回調(diào)活玲;還有兩個(gè)是頁(yè)面CollectionViewCell關(guān)注按鈕點(diǎn)擊的事件回調(diào)。

排除了第一種情況,那么著重分析第二種情況舒憾,最終我們排查出問題在于CollectionViewCell按鈕點(diǎn)擊的事件回調(diào)與MoreBrandListVC對(duì)象造成了循環(huán)引用镀钓,下面的代碼,是因?yàn)殚]包導(dǎo)致了循環(huán)引用珍剑,讀者可以回顧一下掸宛,自己是否寫過這樣的代碼死陆,


func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(kCellReuseIdentify, forIndexPath: indexPath) as? CustomCell
        
        // cell?.focusButtonClickBlock = xxx會(huì)導(dǎo)致循環(huán)引用
        cell?.focusButtonClickBlock = { (model) -> Void in
          self.sendRequest(withModel: model)
        }
        return cell!
    }

上面的代碼招拙,第一眼看起來沒有什么問題,但是正如注釋所言措译,cell?.focusButtonClickBlock = xxx會(huì)導(dǎo)致循環(huán)引用别凤,這段代碼中有這樣幾個(gè)角色,self, cell, focusButtonClickBlock领虹,self代表當(dāng)前的控制器规哪,cell是自定義的UICollectionViewCell,focusButtonClickBlock代表cell上面按鈕點(diǎn)擊的事件回調(diào)塌衰,這樣劃分了角色诉稍,就能弄清3者相互持有的過程,大體是這樣的最疆,

  1. self持有了cell杯巨,
  2. cell持有了focusButtonClickBlock,
  3. focusButtonClickBlock內(nèi)部持有了self努酸。

3者之間的關(guān)系服爷,如下圖所示,

strong-retain-cycle.png

這樣获诈,3者之間就形成了循環(huán)引用仍源,接下來的事情就是打破這個(gè)循環(huán)引用,使用unowned來打破循環(huán)引用舔涎,bug也迎刃而解笼踩,如下代碼所示,


func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(kCellReuseIdentify, forIndexPath: indexPath) as? CustomCell
        
        // 這里添加了[unowned self]
        cell?.focusButtonClickBlock = { [unowned self] (model) -> Void in
          self.sendRequest(withModel: model)
        }
        return cell!
    }

在focusButtonClickBlock內(nèi)部定義[unowned self]打破了三者之間的循環(huán)引用亡嫌,因?yàn)榇藭r(shí)閉包持有的self是unowned修飾嚎于,它表明閉包并不對(duì)self是強(qiáng)引用,而是無主引用昼伴,這樣當(dāng)self即ViewController通過點(diǎn)擊返回按鈕匾旭,在NavigationController的堆棧中出棧時(shí),就不會(huì)因?yàn)檠h(huán)引用導(dǎo)致無法從內(nèi)存中釋放圃郊。打破循環(huán)引用之后价涝,三者之間的關(guān)系如下圖所示,

break-retain-cycle.png

3. 結(jié)尾的釋疑

這里使用了unowned解決了循環(huán)引用持舆,那么為什么不使用weak呢色瘩?其實(shí)也可以使用weak來解決問題伪窖,兩者區(qū)別就是self是否可能為nil,當(dāng)我們點(diǎn)擊cell上面的按鈕時(shí)候可以確保self是存在的居兆,而不是nil覆山,所以使用unowned不會(huì)有什么問題。

但是泥栖,如果在一個(gè)網(wǎng)絡(luò)請(qǐng)求的回調(diào)里面使用unowned簇宽,那么有可能會(huì)導(dǎo)致crash,因?yàn)橛脩粲锌赡茉诰W(wǎng)絡(luò)請(qǐng)求的過程中等的不耐煩吧享,直接從當(dāng)前ViewController頁(yè)面退回前一個(gè)頁(yè)面魏割,這時(shí)候self就是nil,使用unowned導(dǎo)致了崩潰钢颂;而使用weak則不會(huì)有這種問題钞它,因?yàn)閣eak允許被修飾的對(duì)象為nil。

這么說來[weak self]相當(dāng)于self?殊鞭,是可選解包遭垛;而[unowned self]相當(dāng)于self!,是隱式強(qiáng)制解包操灿。

差不多就是這樣吧锯仪。

參考鏈接

公眾號(hào)

微信掃描下方圖片,歡迎關(guān)注本人公眾號(hào)foolishlion牲尺,咱們來談技術(shù)談人生卵酪,因?yàn)檫@又不要錢,

foolishlion.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谤碳,一起剝皮案震驚了整個(gè)濱河市溃卡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蜒简,老刑警劉巖瘸羡,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異搓茬,居然都是意外死亡犹赖,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門卷仑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來峻村,“玉大人,你說我怎么就攤上這事锡凝≌匙颍” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)张肾。 經(jīng)常有香客問我芭析,道長(zhǎng),這世上最難降的妖魔是什么吞瞪? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任馁启,我火速辦了婚禮,結(jié)果婚禮上芍秆,老公的妹妹穿的比我還像新娘惯疙。我一直安慰自己,他們只是感情好浪听,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布螟碎。 她就那樣靜靜地躺著,像睡著了一般迹栓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上俭缓,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天克伊,我揣著相機(jī)與錄音,去河邊找鬼华坦。 笑死愿吹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的惜姐。 我是一名探鬼主播犁跪,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼歹袁!你這毒婦竟也來了坷衍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤条舔,失蹤者是張志新(化名)和其女友劉穎枫耳,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體孟抗,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡迁杨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凄硼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铅协。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖摊沉,靈堂內(nèi)的尸體忽然破棺而出狐史,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布预皇,位于F島的核電站侈玄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏吟温。R本人自食惡果不足惜序仙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鲁豪。 院中可真熱鬧潘悼,春花似錦、人聲如沸爬橡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)糙申。三九已至宾添,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間柜裸,已是汗流浹背缕陕。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留疙挺,地道東北人扛邑。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像铐然,于是被迫代替她去往敵國(guó)和親蔬崩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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

  • 20- 枚舉,枚舉原始值,枚舉相關(guān)值,switch提取枚舉關(guān)聯(lián)值 Swift枚舉: Swift中的枚舉比OC中的枚...
    iOS_恒仔閱讀 2,278評(píng)論 1 6
  • 作為一門現(xiàn)代的高級(jí)編程語(yǔ)言搀暑,Swift代替我們進(jìn)行了對(duì)象的創(chuàng)建和銷毀等相關(guān)的內(nèi)存管理沥阳。它使用了一個(gè)優(yōu)雅的技術(shù),叫做...
    Maru閱讀 2,102評(píng)論 4 17
  • 明天就是圣誕節(jié)啦险掀,于是沪袭,在偏僻的郊區(qū)住了這么久的四只,我們準(zhǔn)備明天進(jìn)城去感受一下圣誕節(jié)的氣氛樟氢。 雖然臨近考試冈绊,應(yīng)該...
    cuckoo醬閱讀 260評(píng)論 0 2
  • 達(dá)羅的意義在于保存住2014年產(chǎn)生的一些觀點(diǎn)。其中有一些具有寓意埠啃。 與簡(jiǎn)書的這個(gè)專題相比較死宣,建立達(dá)羅的豆列在今天被...
    Cyberpunk閱讀 276評(píng)論 0 1
  • 實(shí)習(xí)的時(shí)光正一天一天的縮短,距離返校也越來越近碴开。一想到馬上就要畢業(yè)了毅该,要離開我生活了多年的學(xué)校博秫,離開那個(gè)我曾非常熟...
    SundyZhou閱讀 290評(píng)論 5 1