4-2 Core Data--用 SQLite 和 FMDB 替代 Core Data

憑良心講另绩,我不能告訴你不去使用 Core Data痴晦。它不錯南吮,而且也在變得更好,并且它被很多其他 Cocoa 開發(fā)者所理解誊酌,當有新人加入你的團隊或者需要別人接手你的 app 的時候部凑,這點很重要。

更重要的是碧浊,不值得花時間和精力去寫自己的系統(tǒng)去代替它涂邀。使用 Core Data 吧。真的箱锐。

為什么我不使用Core Data

Mike Ash 寫到

就個人而言比勉,我不是個狂熱粉絲。我發(fā)現(xiàn) (Core Data 的) API 是笨拙的驹止,并且框架本身對于超過一定數(shù)量級的數(shù)據(jù)的處理是極其緩慢的浩聋。

一個實際的例子:10,000 個條目

想象一個 RSS 閱讀器,一個用戶可以在一個 feed 上點擊右鍵臊恋,并且選擇標記所有為已讀衣洁。

實際實現(xiàn)上,我們有一個帶有read屬性的 Article 實體捞镰。把所有條目標記為已讀闸与,app 需要加載這個 feed 的所有文章 (可能通過一對多的關系)毙替,然后設置 read 屬性為 YES岸售。

大部分時候這樣是沒問題的。但是設想那個 feed 有 200 篇文章厂画,為了避免阻塞主線程凸丸,你可能考慮在后臺線程里做這個工作 (尤其是如果這個 app 是一個 iPhone app)。一旦你開始使用 Core Data 多線程的時候袱院,事情就開始變的不好處理了屎慢。

這可能還沒這么糟糕,至少不值得拋棄使用 Core Data忽洛。

但是腻惠,再添加同步。

我用過兩個不同的 RSS 同步 API欲虚,它們返回已讀文章的 uniqueID 數(shù)組集灌。其中一個返回近 10,000 個 ID。

你不會打算在主線程中加載 10,000 篇文章复哆,然后設置read為 NO欣喧。你大概也不會想在后臺線程里加載 10,000 篇文章腌零,即使很小心地管理內(nèi)存。這里有太多的工作(如果你頻繁的這么做唆阿,想一下對電池壽命的影響)益涧。

概念上來說,你真正想要做的是驯鳖,讓數(shù)據(jù)庫將 uniqueID 列表里的每一篇文章的read設置為 YES闲询。

SQLite 可以做到這個,只用一次調(diào)用臼隔。如果uniqueID上有索引嘹裂,這會很快。而且你可以在后臺線程執(zhí)行摔握,這和在主線程執(zhí)行一樣容易寄狼。

另一個例子:快速啟動

我的另一個 app,我想減少啟動時間 — 不只是 app 的啟動時間氨淌,還有數(shù)據(jù)顯示之前所需要的時間泊愧。

這是個類似 Twitter 的 app (雖然它不是):它顯示消息的時間軸。顯示時間軸意味著獲取消息盛正,并加載相關用戶删咱。它很快,但是在啟動的時候豪筝,會填充 UI痰滋,然后填充數(shù)據(jù)。

關于 iPhone app(或者所有應用)续崖,我的理論是敲街,啟動時間比其他大部分開發(fā)者想的都要重要。啟動時間很慢的 app 是不太可能被啟動的严望,因為人們潛意識里會記住多艇,并且在啟動那個應用這件事情上形成一種抵抗心理。減少啟動時間可以減少這種阻力像吻,用戶也會更愿意使用你的應用峻黍,并且把它推薦給其他人。這是你讓你的 app 成功的一部分拨匆。

因為我不使用 Core Data姆涩,我手頭有一個簡單的,保守的解決方案惭每。我把時間軸(消息和人物對象)通過NSCoding保存到一個 plist 文件中骨饿。啟動的時候它讀取這個文件,創(chuàng)建消息和人物對象,UI 一出現(xiàn)就顯示時間軸样刷。

這明顯的減少了延遲仑扑。

把消息和人物對象作為NSManagedObject的實例對象,這是不可能的置鼻。(假設我已經(jīng)編碼并且存儲對象的 IDs镇饮,但是那意味著讀取 plist 文件,之后再涉及數(shù)據(jù)庫箕母。這種方式我完全避免了數(shù)據(jù)庫)储藐。

(在更新更快的機器出來后, 我去掉了那些代碼∷皇牵回顧過去钙勃,我希望我可以把它留下來。)

我怎么考慮這個問題

當考慮是否使用 Core Data聂喇,我考慮下面這些事情:

會有難以置信數(shù)量的數(shù)據(jù)嗎辖源?

對于一個 RSS 閱讀器或者 Twitter app,答案顯而易見:是的希太。有些人關注上百個人克饶。一個人可能訂閱了上千個 feed。

即使你的應用不從網(wǎng)絡獲取數(shù)據(jù)誊辉,用戶仍然有可能自動添加數(shù)據(jù)矾湃。如果你用一個支持 AppleScript 的 Mac,有人會寫腳本去加載非常多的數(shù)據(jù)堕澄。如果通過 web API 去添加數(shù)據(jù)也是一樣的邀跃。

會有一個 Web API 包含類似于數(shù)據(jù)庫的結(jié)果嗎(對比于類似對象的結(jié)果)?

一個 RSS 同步 API 能夠返回一個已讀文章的 uniqueID 列表蛙紫。一個筆記的應用的一個同步 API 可能返回已存檔的和已刪除的筆記的 uniqueID 列表拍屑。

用戶可能通過操作處理大量對象嗎?

在底層惊来,需要考慮和之前一樣的問題丽涩。當有人刪除所有已經(jīng)下載的 5,000 個面食食譜棺滞,你的食譜 app 性能如何裁蚁?(在 iPhone 上?)

如果我決定使用 Core Data(我已經(jīng)發(fā)布過使用 Core Data 的應用)继准,我會特別注意我如何使用它枉证。結(jié)果為了得到好的性能,我發(fā)現(xiàn)我把它當做了一個奇怪接口的 SQL 數(shù)據(jù)庫在使用移必,然后我就知道了室谚,我應該舍棄 Core Data抱究,而去直接使用 SQLite融欧。

我如何使用 SQLite

我通過FMDB Wrapper來使用 SQLite,F(xiàn)MDB 來自 Flying Meat Software,由 Gus Mueller 開發(fā)啸胧。

基本操作

在使用 iPhone 和 Core Data 之前,我就使用過 SQLite谨垃。這里有關于它如何工作的要點:

所有數(shù)據(jù)庫訪問 - 讀和寫 - 發(fā)生在一個后臺線程的連續(xù)的隊列里拯腮。在主線程中觸及數(shù)據(jù)庫是從來不被允許的。使用一個連續(xù)隊列來保證每一件事是按順序發(fā)生的潮售。

我大量使用 blocks 使得異步編程容易些痊项。

模型對象只存在在主線程(但有兩個重要的例外),改變會觸發(fā)一個后臺保存酥诽。

模型對象列出來它們在數(shù)據(jù)庫中存儲的屬性鞍泉。這可能在代碼里或者在 plist 文件里。

有些模型對象是唯一的肮帐,有些不是咖驮。取決于 app 的需要(大部分情況是唯一的)。

對關系型數(shù)據(jù)训枢,我盡可能避免創(chuàng)建查詢表游沿。

一些對象類型在啟動的時候就完全讀入內(nèi)存,另一些對象類型我可能創(chuàng)建和維護的只有它們 uniqueID 的一個 NSMutableSet肮砾,所以我可以在不去碰數(shù)據(jù)庫的情況下就知道什么存在诀黍、什么不存在。

Web API 的調(diào)用發(fā)生在后臺線程仗处,它們使用“分離“的模型對象眯勾。

我會使用我目前的 app的代碼來描述。

數(shù)據(jù)庫更新

在我最近的 app 中婆誓,有一個單一的數(shù)據(jù)庫控制器 -VSDatabaseController吃环,它通過 FMDB 來與 SQLite 對話。

FMDB 區(qū)分更新和查詢洋幻。更新數(shù)據(jù)庫郁轻,app 調(diào)用:


VSDatabaseUpdateBlock很簡單:


runDatabaseBlockInTransaction也很簡單:


(注意我用的自己的連續(xù) dispatch 隊列。Gus 建議看一下FMDatabaseQueue文留,這也是一個連續(xù)調(diào)度隊列好唯。因為它比 FMDB 剩下的其他東西都要新,所以我自己還沒有去看過燥翅。)

beginTransaction和endTransaction的調(diào)用是可嵌套的(在我的數(shù)據(jù)庫控制器里)骑篙。在合適的時候他們會調(diào)用-[FMDatabase beginTransaction]和-[FMDatabase commit]。(使用 transactions 是讓 SQLite 變快的一大關鍵森书。)提示:我在-[NSThread threadDictionary]中存儲當前的 transaction 的計數(shù)靶端。這對于針對每個線程的數(shù)據(jù)來說是很方便的谎势,我也幾乎從不用它做其他的事情。

這兒有個調(diào)用更新數(shù)據(jù)庫的簡單例子:


這說明了不少事情杨名。首先脏榆, SQL 并不可怕。即使你從沒見過它台谍,你也知道這行代碼做了什么姐霍。

像VSDatabaseController的所有其他公共接口一樣,emptyTagsLookupTableForNote也應該在主線程中被調(diào)用典唇。模型對象只能在主線程中被引用镊折,所以在 block 中使用uniqueID,而不是 VSNote 對象介衔。

注意在這種情況下恨胚,我更新了一個查詢表。Notes 和 tags 有一個多對多關系炎咖,一種表現(xiàn)方式是用一個數(shù)據(jù)庫表映射 note uniqueIDs 和 tag uniqueIDs赃泡。這些表不會很難維護,但是如果可能乘盼,我盡量避免使用它們升熊。

注意在更新字符串中的?。-[FMDatabase executeUpdate:]是一個可變參數(shù)函數(shù)绸栅。SQLite 支持使用占位符 - ? 字符 - 所以你不需要把實際的值放入字符串中去级野。這是一個安全上的考量:它可以守護程序避免 SQL 注入。它也可以幫助你減少必須 escape 值這樣的不必要的麻煩粹胯。

最后蓖柔,注意在 tagsNotesLookup 表中,有一個 noteUniquelID 的索引(索引是 SQLite 性能的又一個關鍵)风纠。這行代碼在每次啟動時都調(diào)用:


數(shù)據(jù)庫獲取

要獲取對象况鸣,app 調(diào)用:


這兩行代碼做了大部分工作:


用 FMDB 查找數(shù)據(jù)庫返回一個FMResultSet. 通過 resultSet 你可以逐句循環(huán),創(chuàng)建模型對象竹观。

我建議寫通用的代碼去將數(shù)據(jù)庫中的行轉(zhuǎn)換為對象镐捧。一種我已經(jīng)使用的方法是在 app 中用一個 plist 文件,將列的名字映射到模型對象的屬性上去臭增。它也包含類型懂酱,所以你知道是調(diào)用-[FMResultSet dateForColumn:]還是-[FMResultSet stringForColumn:]或是其他方法。

在我的最新 app 里我做的事情更簡單速址。數(shù)據(jù)庫行剛好對應模型對象屬性的名字玩焰。除了那些名字以 “Date” 結(jié)尾的屬性以外由驹,所有屬性都是字符串芍锚。簡單昔园,但是你可以看到所需要明顯清晰的對應關系。

唯一對象

創(chuàng)建模型對象的操作和從數(shù)據(jù)庫獲取數(shù)據(jù)操作在同樣的后臺線程進行并炮。一但獲取到默刚,app 會把它們轉(zhuǎn)到主線程。

通常我會使用唯一對象逃魄。數(shù)據(jù)庫里的同一行荤西,始終對應著同樣的一個對象。

為了做到唯一伍俘,我使用 NSMapTable 創(chuàng)建了一個對象緩存邪锌,在 init 函數(shù)里:_objectCache = [NSMapTable weakToWeakObjectsMapTable]。我來解釋一下:

例如癌瘾,當你進行一個數(shù)據(jù)庫獲取操作并且把對象轉(zhuǎn)交給一個視圖控制器時觅丰,你希望在這個視圖控制器使用完這些對象后,或者在一個不一樣的視圖控制器被顯示后妨退,這些對象可以消失妇萄。

如果你的對象緩存是一個NSMutableDictionary,那你將需要做一些額外的工作來清空緩存中的對象咬荷。保證它只引用了那些其他地方有引用的對象是一件非常讓人蛋疼的事情冠句。而使用配合弱引用的NSMapTable,這個問題就被自動處理掉了幸乒。

所以:我們在主線程中讓對象唯一懦底。如果一個對象已經(jīng)在對象緩存中存在,我們就用那個存在的對象罕扎。(因為主線程中對象可能有改變基茵,因此在沖突時我們使用主線程的對象。)如果對象緩存中沒有壳影,它會被加上拱层。

保持對象在內(nèi)存中

有很多次,把整個對象類型保留在內(nèi)存中是有道理的宴咧。我最新的 app 有一個 VSTag 對象根灯。雖然可能有成百上千篇筆記,但 tags 的數(shù)量很小掺栅,基本少于十個烙肺。一個 tag 只有 6 個屬性:三個 BOOL,兩個很小的 NSstring氧卧,還有一個 NSDate桃笙。

啟動的時候,app 獲取所有 tags 并且把它們保存在兩個字典里沙绝,其中一個的鍵是 tag 的 uniqueID搏明,另一個的鍵是 tag 名字的小寫鼠锈。

這簡化了很多事,比如 tag 自動補全系統(tǒng)星著,就可以完全在內(nèi)存中操作购笆,而不需要從數(shù)據(jù)庫獲取了。

但是很多次虚循,把所有數(shù)據(jù)保留在內(nèi)存中是不實際的同欠。比如我們不會在內(nèi)存中保留所有筆記。

但是也有很多次横缔,把所有對象保存在內(nèi)存中是不可行的铺遂。當不能在內(nèi)存中保留一個對象類型時,你可能會希望在內(nèi)存中保留所有 uniqueID茎刚,你可以進行這樣一個獲取操作:


resultSet 只包含了 uniqueIDs娃循, 你可以存儲到一個 NSMutableSet 里。

我發(fā)現(xiàn)有時這個對 web APIs 很有用斗蒋。想象一個 API 返回從某個確定的時間以后所創(chuàng)建筆記的 uniqueIDs 列表捌斧。如果我本地已經(jīng)有了一個包含所有筆記 uniqueIDs 的 NSMutableSet,我可以 (通過-[NSMutableSet minusSet]) 快速檢查是否有漏掉的筆記泉沾,然后去調(diào)用另一個 API 下載那些漏掉的筆記捞蚂。這些完全不需要觸及數(shù)據(jù)庫。

但是跷究,像這樣的事情應該小心處理姓迅。app 可以提供足夠的內(nèi)存嗎?它真的簡化編程并且提高性能了嗎俊马?

使用 SQLite 和 FMDB 來代替 Core Data丁存,會給你帶來大量的靈活性和使用更聰明的辦法來解決問題的空間。記住有的時候聰明是好的柴我,也有的時候聰明是一個大錯誤解寝。

Web APIs

我的 API 調(diào)用都跑在后臺進程里(通常是用一個NSOperationQueue,這樣我可以取消操作)艘儒。模型對象只在主線程聋伦,然后將模型對象傳遞給我的 API 調(diào)用。

具體這么做:一個數(shù)據(jù)庫對象有一個detachedCopy方法界睁,可以復制數(shù)據(jù)庫對象觉增。這個復制的對象不會被我用來做唯一化的對象緩存所引用。唯一引用這個對象的地方是 API 調(diào)用翻斟,當 API 調(diào)用結(jié)束時逾礁,這個復制的對象也就消失了。

這是一個好的系統(tǒng)访惜,因為它意味著我可以在 API 調(diào)用里使用模型對象嘹履。方法看起來像這樣:


VSNoteAPICall 從分離出來的VSNote中獲取值腻扇,并且創(chuàng)建 HTTP 請求,而不是將 note 包裝成一個字典或其他表現(xiàn)形式植捎。

處理 Web API 的返回值

我對 web 的返回值做了一些類似的處理衙解。我會對返回的 JSON 或者 XML 創(chuàng)建一個模型對象阳柔,這個模型對象也是分離的焰枢。它沒有存儲在唯一化模型緩存里。

這里有些事情是不確定的舌剂。有時我們需要用那個模型對象在在內(nèi)存緩存以及數(shù)據(jù)庫兩個地方做本地修改济锄。

數(shù)據(jù)庫通常是容易的部分。比如:我的 app 已經(jīng)有一個方法來保存筆記對象霍转。它使用 SQL 的insert or replace命令荐绝。我只需用從 web API 返回值所生成的筆記對象來進行調(diào)用,數(shù)據(jù)庫就會更新避消。

但是可能同樣的對象在內(nèi)存中還有一個版本低滩,幸運的是我們很容易找到它:


如果 cachedNote 存在,我會讓它從 downloadedNote中 獲取值(這部分可以共享detachedCopy方法的代碼岩喷。)恕沫,而不是直接替換它(這樣可能違反唯一性)。

一旦 cachedNote 更新了纱意,觀察者會通過 KVO 察覺到變化婶溯,或者我會發(fā)送一個NSNotification,或者兩者都做偷霉。

Web API 調(diào)用也會返回一些其他值迄委。我提到過 RSS 閱讀器可能獲得一個已讀條目的大列表。這種情況下类少,我選擇通過那個列表創(chuàng)建一個NSSet叙身,在內(nèi)存的緩存中更新每一個緩存文章的read屬性,然后調(diào)用-[FMDatabase executeUpdate:]硫狞。

完成這個工作的關鍵是NSMapTable的查找是快速的曲梗。如果你找的對象在一個 NSArray 里,我們就得重新考慮考慮了妓忍。

數(shù)據(jù)庫遷移

當正常工作的時候虏两,Core Data 的數(shù)據(jù)庫遷移功能還是蠻酷的。

但是不可避免的世剖,它在代碼和數(shù)據(jù)庫中加入了一層定罢。如果你更直接一點,去使用 SQLite旁瘫,那么更新數(shù)據(jù)庫也就變得越直接祖凫。

你可以安全容易的做到這點琼蚯。

比如加一個表:


或添加一個索引


或添加一列:


app 應該用類似上面這樣的代碼來首先對數(shù)據(jù)庫進行設置。以后的改變就是添加對 executeUpdate 的調(diào)用 — 我讓他們按順序執(zhí)行惠况。因為我的數(shù)據(jù)庫是我設計的遭庶,所以這不會有什么問題(我從沒碰到性能問題,它很快)稠屠。

當然大的改變需要更多代碼峦睡。如果你的數(shù)據(jù)通過 web 獲取,有時你可以從一個新數(shù)據(jù)庫模型開始权埠,重新下載你需要的數(shù)據(jù)榨了。

性能技巧

SQLite 可以非常非常快攘蔽,但是也可以非常慢龙屉。完全取決于你怎么使用它。

事務

把更新包裝在事務里满俗。在更新前調(diào)用-[FMDatabase beginTransaction]转捕,更新后調(diào)用-[FMDatabase commit]。

如果你不得不反規(guī)范化( Denormalize)

反規(guī)范化讓人很不爽唆垃。這個方法是五芝,為了加速檢索而添加冗余數(shù)據(jù),但是它意味著你需要維護冗余數(shù)據(jù)降盹。

我總是盡力避免它与柑,直到這樣能有嚴重的性能差異。然后我會盡可能少得這么做蓄坏。

使用索引

我的 app 中 tags 表的創(chuàng)建語句像這樣:


uniqueID 列是自動索引的价捧,因為它定義為 unique。但是如果我想用 name 來查詢表涡戳,我可能會在name上創(chuàng)建一個索引结蟋,像這樣:


你可以一次性在多列上創(chuàng)建索引,像這樣:


但是注意太多索引會降低你的插入速度渔彰。你只需要足夠數(shù)量并且是正確的那些嵌屎。

使用命令行應用

當我的 app 在模擬器里運行時,我會用NSLog輸出數(shù)據(jù)庫的路徑恍涂。我可以通過 sqlite3 的命令行來打開數(shù)據(jù)庫宝惰。(通過 man sqlite3 命令來了解這個應用的更多信息)。

打開數(shù)據(jù)庫的命令:sqlite3 path/to/database再沧。

打開以后尼夺,你可以輸入.schema來查看 schema。

你可以更新和查詢,這是在你的 app 使用 SQL 之前就將它們正確地準備妥當?shù)暮芎玫姆绞健?/p>

這里面最酷的一部分是淤堵,SQLite Explain Query Plan 命令寝衫,你會希望確保你的語句執(zhí)行的盡可能快。

真實的例子

我的 app 顯示所有沒有歸檔筆記的標簽列表拐邪。每當筆記或者標簽有變化慰毅,這個查詢就會重新執(zhí)行一次,所以它需要很快扎阶。

我可以用SQL join來查詢汹胃,但是這會很慢(join 都很慢)。

所以我放棄 sqlite3 并開始嘗試別的方法乘陪。我又檢查了一次我的 schema统台,意識到我可以反規(guī)范化雕擂。一個筆記的歸檔狀態(tài)可以存儲在 notes 表里啡邑,它也可以存儲在 tagsNotesLookup 表。

然后我可以執(zhí)行一個查詢:


我已經(jīng)有了一個在 tagUniqueID 上的索引井赌。所以我用 explain query plan 來告訴我當我執(zhí)行這個查詢的時候會發(fā)生什么谤逼。


它用了一個索引,這很不錯仇穗,但是 SCAN TABLE 聽起來不太好流部,最好是一個 SEARCH TABLE 加上覆蓋索引的方式。

我在 tagUniqueID 和 archive 上建了索引:


再次執(zhí)行 explain query plan:


現(xiàn)在好多了纹坐。

更多性能提示

FMDB 的某處加了緩存 statements 的能力枝冀,所以當創(chuàng)建或打開一個數(shù)據(jù)庫的時候,我總是調(diào)用[self.database setShouldCacheStatements:YES]耘子。這意味著對每個調(diào)用你不需要再次編譯每個 statement果漾。

我從來沒有找到關于使用vacuum的好的指引。如果數(shù)據(jù)庫沒有定期壓縮谷誓,它會變得越來越慢绒障。我的 app 會每周跑一次 vacuum。(在 NSUserDefaults 里存儲上次 vacuum 的時間捍歪,然后在開始的時候檢查是否過了一周)户辱。

使用auto_vacuum可能會更好,可以參看pragma statements supported by SQLite列表糙臼。

其他酷的東西

Gus Mueller 讓我講講自定義 SQLite 方法的內(nèi)容庐镐。我并沒有真的使用過這些東西,不過既然他指出了变逃,我可以放心的說我能找到它的用處必逆。因為它很酷。

在 Gus 的這個 gist 里,有一個查詢是這樣的:


SQLite 完全不知道 UTTypes 的事情末患。但是你可以通過代碼塊來添加核心方法研叫,感興趣的話,可以看看-[FMDatabase makeFunctionNamed:maximumArguments:withBlock:]方法璧针。

你可以執(zhí)行一個大的查詢來替代嚷炉,然后評估每個對象 - 但是那需要更多工作。最好在 SQL 級就過濾探橱,而不是在將表格行轉(zhuǎn)為對象以后再做這件事情申屹。

最后

你真的應該使用 Core Data,我不是在開玩笑隧膏。

我用 SQLite 和 FMDB 一段時間了哗讥,我對多得到的好處感到很興奮,也得到非同一般的性能胞枕。

但是記住設備在不斷變快杆煞。也請記住,其他看你代碼的人期望看到 Core Data腐泻,這是他們已經(jīng)了解的 - 他們不打算看你的數(shù)據(jù)庫代碼如何工作决乎。

所以請把這整篇文章看做一個瘋子的叫喊,關于他為自己建立了充滿細節(jié)又瘋狂的世界 - 并把自己鎖在了里面派桩。

有點難過的搖頭构诚,并且請享受這個話題下那些超贊的 Core Data 的文章吧。

而對我來說铆惑,接下來在研究完 Gus 指出的自定義 SQLite 方法特性后范嘱,我會研究 SQLite 的全文搜索擴展。 總有更多的內(nèi)容需要不斷去學習员魏。


翻譯作者:answer-huang

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末丑蛤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子逆趋,更是在濱河造成了極大的恐慌盏阶,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闻书,死亡現(xiàn)場離奇詭異名斟,居然都是意外死亡,警方通過查閱死者的電腦和手機魄眉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門砰盐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坑律,你說我怎么就攤上這事岩梳。” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵冀值,是天一觀的道長也物。 經(jīng)常有香客問我,道長列疗,這世上最難降的妖魔是什么滑蚯? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮抵栈,結(jié)果婚禮上告材,老公的妹妹穿的比我還像新娘。我一直安慰自己古劲,他們只是感情好斥赋,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著产艾,像睡著了一般疤剑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胰舆,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天骚露,我揣著相機與錄音蹬挤,去河邊找鬼缚窿。 笑死,一個胖子當著我的面吹牛焰扳,可吹牛的內(nèi)容都是我干的倦零。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼吨悍,長吁一口氣:“原來是場噩夢啊……” “哼扫茅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起育瓜,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤葫隙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后躏仇,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體恋脚,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡招盲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年处嫌,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伙单。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡书妻,死狀恐怖船响,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤见间,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布聊闯,位于F島的核電站,受9級特大地震影響米诉,放射性物質(zhì)發(fā)生泄漏馅袁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一荒辕、第九天 我趴在偏房一處隱蔽的房頂上張望汗销。 院中可真熱鬧,春花似錦抵窒、人聲如沸弛针。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽削茁。三九已至,卻和暖如春掉房,著一層夾襖步出監(jiān)牢的瞬間茧跋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工卓囚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留瘾杭,地道東北人。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓哪亿,卻偏偏與公主長得像粥烁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蝇棉,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,284評論 25 707
  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫讨阻、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,120評論 4 61
  • 生活中總會有這樣一些人篡殷,把熱忱钝吮、耐心和笑臉留給了外人,把冷漠板辽、無情和自私帶回了家里奇瘦!這又何必呢! ...
    麥柒柒閱讀 820評論 3 7
  • 我是一個悲觀主義戳气,覺得一點小事就會讓我不開心链患,特別容易難過,特別容易悲傷瓶您,外面的天氣晴朗麻捻,我的世界在下雨
    陽光盛開的藍天閱讀 223評論 0 0
  • 學習筆記系列#6分類:成長 1/答案是時間嗎贸毕? 點開這個鏈接之前郑叠,我都能想象到你的不屑。答案很明顯嘛明棍,肯定是說時間...
    兜帥宮閱讀 234評論 0 0