讓你的 iOS 庫支持 pod 和 carthage

主流的依賴管理有三大開源庫:最老牌的 CocoaPods, 新秀 Carthage, 官方的 Swift Package Manager(目前只支持 macOS允青,不予討論)玛迄。

讓我們的庫支持這兩種依賴管理方式需要特定的工程形式嗎?比如一個動態(tài)庫:

Cocoa Touch Framework.png

這不是必需的宁舰,CocoaPods 和 Carthage 有自己的規(guī)則去獲取源代碼文件和資源文件拼卵,并不依賴這種特殊的封裝,對 CocoaPoda 而言蛮艰,甚至不需要庫以工程的形式存在腋腮,而對 Carthage 來說,庫的代碼和資源文件必須通過工程的形式來獲取壤蚜。

CocoaPods

CocoaPods 是最早出現(xiàn)的即寡,也是目前影響力最大的依賴管理庫。CocoaPod 的官方教程:Making a CocoaPod袜刷。

面對一堆代碼和資源文件聪富,需要三個步驟讓別人通過 pod 安裝:

  1. 創(chuàng)建 .podspec 配置文件(spec 是 specification 的縮寫):庫名,版本水泉,托管地址善涨,源碼和資源在托管地址的相對位置;
  2. 托管到網(wǎng)絡(luò)上草则,比如 Git钢拧;
  3. 發(fā)布到 pod 的 trunk 服務(wù)器

第2步結(jié)束后,別人就可以通過 pod 來使用你的庫炕横,而實現(xiàn)了第3步后源内,可以實現(xiàn)最簡單的安裝方式:pod 'libraryName'

在托管到網(wǎng)絡(luò)之前份殿,我們需要測試下庫是否能夠正常接入和使用膜钓;發(fā)布后,更新版本前需要進行測試卿嘲,所以支持本地使用是很重要的颂斜,pod 支持。

本地開發(fā)階段

使用 pod 接入第三方庫是通過 Podfile 文件配置拾枣,而讓你的庫支持 pod 則是通過 libraryName.podspec 文件進行配置沃疮,這兩個配置文件不要用 TextEdit 編輯盒让,最好使用 Xcode 來編輯。

在開發(fā)階段的通常做法是:創(chuàng)建一個文件夾作為庫的根目錄司蔬,將庫源碼單獨放在一個子文件夾里邑茄,另建一個子文件夾作為 demo 的目錄;在根目錄下添加 libraryName.podspec 文件(可以通過這個命令創(chuàng)建:pod spec create libraryName)俊啼,在 demo 目錄下添加一個 Podfile肺缕。pod 為此提供了一個模版:pod lib create libraryName,它創(chuàng)建了一個完整的占位工程以及配置文件授帕,如果是從頭開始新的項目同木,可以用這個命令省去很多工作,如下所示跛十,我通過這個命令創(chuàng)建了一個SDEDownloader測試庫泉手,這個命令會有一些交互式的配置選項,右側(cè)為最終的目錄結(jié)構(gòu):

podTemplate.png

需要接入這個本地的庫時偶器,在 demo 目錄下通過pod install安裝 Podfile 中指定的本地庫和其它庫,Podfile 里的配置如下:

pod 'libraryName', :path => 'podspecFolderPath'

podspecFolderPath以 Podfile 所在目錄為基礎(chǔ)路徑缝裤,以上面的測試庫為例的話屏轰,這個值為../(也就是這個庫的根目錄),而且這個指定的路徑下必須有 libraryName.podspec 文件憋飞,以及一個LICENSE霎苗。

建議使用上面提到的兩個命令來創(chuàng)建 .podspce 文件,很多必須的配置項都有了榛做,詳細的配置項可參考:Podspec Syntax Reference唁盏。其中的關(guān)鍵必需配置項如下:

// .podspec 文件本身的文件名也必須是 libraryName
s.name = 'libraryName'
// 當(dāng)前庫的版本,使用本地庫時該值被忽略
s.version = '0.1.0'
// 庫的主頁检眯,使用本地庫時厘擂,這個值被忽略
s.homepage = 'https://github.com/seedante/SDEDownloader'
// 庫的托管地址,使用本地庫時锰瘸,這個值被忽略刽严。
// 這里使用了 git,還支持 svn, http, hg避凝。
s.source = { :git => 'https://github.com/seedante/libraryName.git', :tag => s.version.to_s }
// 源代碼文件舞萄,多個值之間使用,分割
s.source_files = 'libraryName/Classes/**/*'
// 排除文件
s.exclude_files = 'Tests'
// 資源文件,還有一個可替代配置 resource_bundles管削,該如何選擇呢倒脓?后面來討論
s.resources = ['Assets/*.xcassets', 'LocalizableFiles/**/*.strings']

podspec 的語法比較寬松,比如上面的值含思,你會看到有的庫里使用''崎弃,有的使用""甘晤,無所謂,選個你喜歡的就行吊履;有多個值時可以放在[]內(nèi)安皱,也可以不用。

pod 根據(jù) .podspec 文件里source提供的庫地址結(jié)合source_files, exclude_files, resources這幾個值(除了source_files艇炎,剩下兩個是可選的)去獲取文件酌伊,這幾個值都是使用相對位置,以 libraryName.podspec 所在目錄為基礎(chǔ)路徑缀踪。在使用本地庫時居砖,source 值被忽略,直接抓取podspecFolderPath目錄下驴娃,通過source_files, exclude_files, resources這幾個值指定的源文件奏候。在這一階段,可以檢查獲取的源文件是否符合預(yù)期唇敞,如何利用通配符來指定源文件可參考:File patterns蔗草。

使用本地庫時,代碼文件會按照物理目錄去整理疆柔,而使用基于網(wǎng)絡(luò)托管的庫時咒精,代碼文件不再按照物理目錄那樣去整理,而是統(tǒng)一在一個目錄下旷档;對于使用resources指定的資源文件模叙,都被會集中放在名為 Resources 的邏輯文件夾下。在工程里鞋屈,通過 pod 安裝的庫都被集中在 Pods 這個工程下范咨,其中安裝的本地庫在Development Pods這個目錄下,其它庫在Pods目錄下厂庇。

PodStructureInXcode.png

托管到網(wǎng)絡(luò)

代碼可以發(fā)布后渠啊,將其托管到網(wǎng)絡(luò)上,比如 Git权旷,為了盡可能減少錯誤昭抒,發(fā)布之前可以先進行測試,在上一個階段炼杖,可以測試是否按照預(yù)期的那樣獲取源文件灭返,現(xiàn)在可以利用pod spec lint來過濾掉一些簡單的無意義的默認值,在 .podspec 文件所在目錄也就是庫的根目錄下運行測試命令坤邪。

下面這些值是使用pod lib create SDEDownloader創(chuàng)建的 .podspec 的默認值:

s.version = '0.1.0'
s.summary = 'A short description of SDEDownloader.'
s.home = 'https://github.com/seedante/SDEDownloader'
s.source = { :git => 'https://github.com/seedante/SDEDownloader.git', :tag => s.version.to_s }

運行pod spec lint后會出現(xiàn)1個錯誤和2個警告:錯誤是無法訪問source指定的地址熙含,因為我們還沒有將代碼發(fā)布到 Github 上;其中一個警告是要求summary修改掉默認值艇纺,寫點有意義的怎静,另外一個警告是home指定的地址無法訪問邮弹,理由和source相同,這里可以填一個可以訪問的地址就可以消除警告蚓聘,當(dāng)然還是保留這個有意義的地址最好腌乡。

這里homesource里相關(guān)的地址就是我們推送到 Git 后的地址,如果你是自己寫夜牡,這些地址也是很好推斷出來的与纽。如果沒有其它問題了,可以發(fā)布代碼了塘装,這里需要注意的是記得添加 tag 來定制版本號急迂,需要與version值匹配:

git tag '0.1.0'
git push --tags

推送到 Github 后最好再次運行測試命令pod spec lint來進行檢查并修改,熟悉流程以后這些值都可以提前填好蹦肴。

現(xiàn)在別人就可以通過 pod 來使用這個庫了僚碎,直接指定庫的地址,語法如下:

pod 'SDEDownloader', :git => 'https://github.com/seedante/SDEDownloader.git', :tag => '0.1.0'

不指定 tag 的話阴幌,使用 repo 下 master branch 最新 commit 里的 .podspec 獲取源文件和資源勺阐;指定 tag 后,則根據(jù)指定版本的 .podspec 獲取源文件和資源矛双。

這種直接指定庫地址的使用方式里皆看,.podspec 里的source值依然被忽略了,你填個其它地址也沒有問題背零。

關(guān)于 .podspec 文件在代碼庫中的位置,前面提到將其放到根目錄下无埃,這是 pod 在File patterns 里硬性規(guī)定的:

Podspecs should be located at the root of the repository, and paths to files should be specified relative to the root of the repository as well.

畢竟不放在根目錄下的話就太麻煩了徙瓶,這個在創(chuàng)建 .podspec 文件時文檔就應(yīng)該指出的,我當(dāng)時想把這個文件挪到其它位置嫉称,花了差不多一天時間才試驗出來侦镇,結(jié)果試驗完了后才看到這條規(guī)定,唉织阅,總是會發(fā)生這種事情......

發(fā)布到 Trunk 服務(wù)器

不發(fā)到 Trunk 服務(wù)器也可以像上一階段那樣通過指定具體的庫地址來使用壳繁,不過為什么還要發(fā)布呢?我也說不上來荔棉,可以看看官方的解釋:CocoaPods Trunk闹炉。

完成這最后一步非常簡單恬汁,前提是這個名字還沒有被其他人搶注:

pod trunk push SDEDownloader.podspec

如果你沒有在 pod 注冊過侣监,運行上面這行命令后會得到提示的,按照提示做即可蟋字。.podspec 里的source值應(yīng)該是在這里起作用壹若,這個庫名就和庫的地址進行了綁定嗅钻。

發(fā)布到 Trunk 服務(wù)器后皂冰,不必指定庫的地址就可以使用:

pod 'SDEDownloader'

Carthage

CocoaPods 會改變工程結(jié)構(gòu),將第三方庫與當(dāng)前的工程納入同一個 workspace 里养篓,而我們其實僅僅需要的是封裝好的庫秃流,Carthage 做的就是這件事,這樣讓通過 Carthage 使用第三方庫的時候比較麻煩柳弄,但是讓你的庫支持 Carthage 無比簡單舶胀,不需要配置文件,只需要將需要共享的源碼和資源所在的 scheme 標(biāo)記為Shared就可以了:

SharedScheme.png

在 Cartfile 里指定第三方庫的語法是這樣的:

github "seedante/SDEDownloader"

它會到 Github 的這個 repo 根目錄下的 .xcworkspace 或者 .xcodeproj 里尋找 shared 的 scheme 里獲取源文件和資源语御,如何確保文件在這個 scheme 里呢峻贮,看文件的歸屬,添加到這個工程的文件基本上是了:

TargetMembership.png

這個支持過程太簡單了应闯,我當(dāng)初都沒有進行過本地測試纤控,Carthage 當(dāng)然也支持使用本地的庫:

// Use a local project
git "file:///directory/to/project" 

不過有幾個限制條件:

  1. 必須納入 git 管理;
  2. 指定路徑是 .xcworkspace 或者 .xcodeproj 所在目錄的絕對路徑碉纺;
  3. 在這種沒有附加任何條件的情況下船万,庫必須用 tag 來劃分版本,即使上面的語法里沒有指定版本骨田,而上面的語法將使用最新版本號下的版本耿导,如果你提交了一個新的 commit,但是卻沒有給這個 commit 添加 tag态贤,上面的語法仍然使用最近的一個 tag 指定的版本而不是最新的 commit舱呻。

關(guān)于版本的指定,Carthage 還支持 branch 和 commit id悠汽,使用非常簡單:

// 將獲取這個 branch 下最新的 commit 的版本
git "file:///directory/to/project", "branchName"
// 將獲取指定的 commit 的版本
git "file:///directory/to/project", "commit_id"

加上 tag箱吕,一共三種方法來指定具體的版本,這三種方法不能混合使用柿冲。

Carthage 支持多種形式的庫地址茬高,詳細可以看 example-cartfile

讓你的工程同時支持 CocoaPods 和 Carthage 并沒有什么沖突假抄,比較麻煩點的地方是Carthage 要在 repo 的根目錄下尋找 .xcworkspace 或者 .xcodeproj 文件怎栽,而 CocoaPods 只需要有 .podspec 文件就可以了,而你通過pod lib create SDEDownloader創(chuàng)建庫文件夾時這兩種文件是在子目錄下的宿饱,把它們移動到根目錄還是有點麻煩的熏瞄。

對圖像文件的支持

在資源文件中,圖像文件有點特殊谬以,比如為了應(yīng)對不同的設(shè)備需要準備多種分辨率的版本巴刻。為了更好地管理資源文件,Xcode 引入了 Asset Catalog蛉签,使用它來管理圖像文件有如下優(yōu)點:

  1. 在更方便的界面里管理適應(yīng)不同設(shè)備的圖像文件沥寥,比在 Xcode Navigationer 里維護多個版本的文件要省心;
  2. 可以直接在控制面板里設(shè)置相關(guān)屬性邑雅,不必再去代碼里設(shè)置。
  3. 官方的 App 瘦身技術(shù)需要使用 Asset Catalog淮野。

在 Xcode 里 Asset Catalog 以 .xcassets 的格式存在吹泡,在進行編譯后,主工程和其它庫里所有的 .xcassets 文件各自都集中成了一個單獨的文件 Assets.car爆哑,那么庫里的 Assets.car 會和主工程下的 Assets.car 沖突嗎,會發(fā)生覆蓋的情況嗎队贱?

沒有放在 .xcassets 文件里的圖像文件在編譯后則依然以原始的形式存在,類似的問題來了柱嫌,庫和主工程會發(fā)生同名文件的沖突嗎屯换?

pod 有兩個屬性用于指定資源文件编丘,分別是resourcesresource_bundles,后者是為了避免命名沖突設(shè)計的彤悔,它引入了命名空間的概念嘉抓,我們可以將資源像使用字典分類。官方強烈建議使用resource_bundles來打包資源蜗巧,但并沒有注明原因以及適用范圍。

之前搜到了這篇2015年的文章《給 Pod 添加資源文件》蕾盯,pod 以往似乎直接將資源文件放主工程里幕屹,也就是 app 的根目錄下,這樣第三方庫里的資源文件可能與主工程里的資源文件發(fā)生命名沖突级遭,使用resource_bundles來解決這個問題,它會將資源文件打包成這樣的文件:你指定的文件名.bundle挫鸽,這樣基本可以解決命名沖突了。

而我摸索的結(jié)果表明這樣的手法完全沒有必要丢郊,不過《給 Pod 添加資源文件》這篇文章作為前期的參考在我寫這部分內(nèi)容時給忘了医咨,直到昨天微信公眾號「知識小集」推送了一篇文章《 Pod 中資源引入方式對比》,里面運用了resource_bundles來解決類似的問題架诞,想起來跟我這篇文章后面的結(jié)論相反,于是我下載了文章里的 Demo 進行了一番測試谴忧。鼓搗了一番后發(fā)現(xiàn):雙方都沒有錯很泊,但是我們都只考慮了一半,癥結(jié)在于我們以不同的方式編譯庫沾谓。于是我重寫了這部分委造。

從 iOS 8 和 Xcode 6 開始引入了 Cocoa Touch Framework,也就是我們常說的動態(tài)庫均驶,它和以往的靜態(tài)庫 Cocoa Touch Static Library 有什么區(qū)別呢昏兆,這又和這篇文章有什么聯(lián)系呢?

簡單來說辣恋,編譯后的靜態(tài)庫不包含資源文件亮垫,它的資源文件都移動到了 app 的根目錄里,所以在 pod 里需要resource_bundles這種解決方案:庫里所有的 .xcassets 文件集中成一個文件 Assets.car伟骨,以及其它以原始形式存在的圖像文件都以"你指定的文件名.bundle"這樣的形式存在饮潦,放在 main bundle(app 根目錄下);如果不使用這樣的方法封裝携狭,而是resource继蜡,庫里的 Assets.car 不會拷貝到 app 的根目錄里,而其它以原始形式的圖像文件會被拷貝逛腿,如果主工程下有同名的文件稀并,庫的同名文件會覆蓋這些文件。

動態(tài)庫處理 .xcassets 文件和原始形式的圖像文件的方式和靜態(tài)庫一樣单默,只不過動態(tài)庫可以包含資源文件碘举,也就是說主工程和動態(tài)庫獨立地存放和管理各自的資源文件,不會發(fā)生沖突搁廓,所以resource_bundles這種解決方法就不需要了引颈。

在文件形式上,靜態(tài)庫是 xxx.a 這樣的格式境蜕,無法在 Finder 里查看蝙场;動態(tài)庫是 xxx.framework 這樣的格式,可以在 Finder 里查看它的內(nèi)容粱年。

Xcode 直到9才支持包含 Swift 代碼的靜態(tài)庫售滤,由于之前我的探索是基于動態(tài)庫,而上面提到的兩種文章里都使用的是靜態(tài)庫,我們雙方都只探討了一半內(nèi)容完箩,現(xiàn)在把兩種情況綜合一下:

Carthage 目前只支持動態(tài)庫赐俗,當(dāng)然它也能將庫編譯為靜態(tài)庫秃励,但如果庫里有資源文件币励,由于在其網(wǎng)頁里沒明確提及這方面的事情,我還不知道怎么處理(后續(xù)有空的話補上這部分內(nèi)容)仅胞;在 pod 里編譯動態(tài)庫需要在 Podfile 里添use_frameworks! 椎眯,如果沒有這句乳丰,則編譯為靜態(tài)庫。

在 pod 里拂酣,使用動態(tài)庫的話埃撵,一切都很簡單,使用resouces打包資源即可虽另;使用靜態(tài)庫時暂刘,如果庫里使用了 .xcassets,則必須使用resource_bundles捂刺,不然庫中 .xcassets 里的圖像都無法使用谣拣,而以原始形式存在的圖像文件,考慮到會與主工程下的文件發(fā)生命名沖突族展,推薦使用resource_bundles森缠。

訪問使用resource_bundles打包的資源會麻煩一點贵涵,而且使用resource_bundles還需要考慮不同庫之間的 bundle 名沖突,建議盡量使用動態(tài)庫來避免這種麻煩恰画。接下來使用例子來講解resourcesresource_bundles兩種方案的使用和區(qū)別宾茂。

CocoaPods: resources, or resource_bundles?

resource_bundles的語法如下,和resources一樣有復(fù)數(shù)形式拴还,其實也沒那么嚴格跨晴,之前一直沒注意,下面是官方的例子自沧,我加了點使用 Asset Catalog 管理的文件:

spec.resource = 'Resources/HockeySDK.bundle'
spec.resources = ['Images/*.png', 'Sounds/*', 'Assets/*.xcassets']

spec.ios.resource_bundle = { 'MapBox' => 'MapView/Map/Resources/*.png' }
spec.resource_bundles = {
    'MapBox' => ['MapView/Map/Resources/*.png', 'Assets/*.xcassets'],
    'OtherResources' => ['MapView/Map/OtherResources/*.png', 'Assets/*.xcassets']
}

對于resources坟奥,使用靜態(tài)庫時,資源文件直接拷貝到 app 的根目錄下拇厢;使用動態(tài)庫時爱谁,則放在庫文件 LibraryName.framework 的根目錄下。

對于resource_bundles孝偎,資源文件時文件被以"MapBox.bundle"和"'OtherResources.bundle"這樣的形式封裝访敌,編譯靜態(tài)庫時,這兩個文件存放在 app 的根目錄下衣盾;使用動態(tài)庫時寺旺,這兩個文件放在庫文件LibraryName.framework 的根目錄下。

可以這樣查看這些內(nèi)容在動態(tài)庫里是如何組織的:在 Pods 工程下(在Xcode里看) Products 目錄下找到 LibraryName.framework势决,右鍵菜單中選擇"Show in Finder"阻塑,在 Finder 里點擊打開。

pod 對resources打包后的結(jié)構(gòu):

LibraryName.framework
--xxxx
--*.png
--Assets.car//所有使用 Asset Catalog 的圖像文件都集中成了這一個文件

使用resource_bundles打包的結(jié)構(gòu):

LibraryName.framework
--xxxx
--*.png
--MapBox.bundle(other.file/*.png/Assets.car)//MapBox指定的所有 .xcassets 文件也集中成了一個文件
--OtherResources.bundle(other.file/*.png/Assets.car)

如何讀取這些圖像呢果复?

UIImage 的方法init?(named: String, in: Bundle?, compatibleWith: UITraitCollection?)可以指定具體的 bundle(其實就是一個文件夾陈莽,相對地每個庫也可以視作一個 bundle),init?(named: String)是這個方法的便捷形式,它在 main bundle (也就是主工程里走搁,app 的根目錄)尋找圖像独柑,這兩個方法優(yōu)先在 bundle 里的 Assets.car 里尋找,找不到后再在 bundle 根目錄下里查找私植。

使用resources打包時忌栅,庫內(nèi)外的代碼這樣訪問庫里的圖像文件:

// 找到 frameworkBundle 的所在位置
let frameworkBundle = Bundle(for: classInLibrary.self)
// init?(named:in:compatibleWith:) 這個方法會優(yōu)先在 frameworkBundle 里面的 Assets.car 里查找,
// 如果沒有找到再在 frameworkBundle 的根目錄下查找曲稼,找到后會緩存起來索绪。
let image = UIImage(named: "imageName", in: frameworkBundle, compatibleWith: nil)

而使用resource_bundles打包的文件額外打包了一層,無論在庫內(nèi)部還是外部贫悄,使用它們需要多一次解包:

// 在 frameworkBunlde 內(nèi)部的位置者春,withExtension 參數(shù)使用'.bundle'也可以
let mapBoxBundle = Bundle.init(url: frameworkBundle.url(forResource: "MapBox", withExtension: "bundle")!)
let image = UIImage(named: "imageName", in: mapBoxBundle, compatibleWith: nil)

在開發(fā) SDEDownloadManager 這個庫時,我將庫源碼單獨用 Cocoa Touch Framework 打包了清女,在庫的內(nèi)部使用圖像文件只需要一次解包钱烟,而使用resource_bundles的話,通過 pod 安裝的庫則需要多一次解包才能使用內(nèi)部的資源嫡丙,這就造成了開發(fā)代碼和發(fā)布代碼不一致拴袭。所以總的來講,使用動態(tài)庫的時候曙博,沒有使用resource_bundles的必要拥刻。

這里還有一點比較有趣,如果使用靜態(tài)庫的話父泳,上面的 frameworkBundle 指向的路徑和mainBundle是一樣的般哼;而動態(tài)庫里,frameworkBundle 指向庫文件的路徑惠窄。

One More Thing

使用 Asset Catalog 有諸多優(yōu)點蒸眠,比如init?(named:in:compatibleWith:)會緩存圖像,有時候圖像只需要使用一次杆融,這時候需要使用init?(contentsOfFile: String)楞卡,這個方法不會緩存數(shù)據(jù),每次都會從指定路徑(.framework 以及 .bundle 里文件的路徑可以利用Bundle這個類獲取)加載圖像脾歇,但這個方法無法對使用 Asset Catalog 的圖像使用蒋腮,因為 Assets.car 是個不透明的文件格式,無法獲取里面的圖像文件的路徑藕各。所以池摧,使用init?(contentsOfFile: String)獲取圖像時,這個圖像文件不要放在 xcassets 里激况。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末作彤,一起剝皮案震驚了整個濱河市踢京,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宦棺,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件黔帕,死亡現(xiàn)場離奇詭異代咸,居然都是意外死亡,警方通過查閱死者的電腦和手機成黄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門呐芥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人奋岁,你說我怎么就攤上這事思瘟。” “怎么了闻伶?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵滨攻,是天一觀的道長。 經(jīng)常有香客問我蓝翰,道長光绕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任畜份,我火速辦了婚禮诞帐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘爆雹。我一直安慰自己停蕉,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布钙态。 她就那樣靜靜地躺著慧起,像睡著了一般。 火紅的嫁衣襯著肌膚如雪册倒。 梳的紋絲不亂的頭發(fā)上完慧,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機與錄音剩失,去河邊找鬼屈尼。 笑死,一個胖子當(dāng)著我的面吹牛拴孤,可吹牛的內(nèi)容都是我干的脾歧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼演熟,長吁一口氣:“原來是場噩夢啊……” “哼鞭执!你這毒婦竟也來了司顿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤兄纺,失蹤者是張志新(化名)和其女友劉穎大溜,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體估脆,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡钦奋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了疙赠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片付材。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖圃阳,靈堂內(nèi)的尸體忽然破棺而出厌衔,到底是詐尸還是另有隱情,我是刑警寧澤捍岳,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布富寿,位于F島的核電站,受9級特大地震影響锣夹,放射性物質(zhì)發(fā)生泄漏作喘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一晕城、第九天 我趴在偏房一處隱蔽的房頂上張望泞坦。 院中可真熱鬧,春花似錦砖顷、人聲如沸贰锁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽豌熄。三九已至,卻和暖如春物咳,著一層夾襖步出監(jiān)牢的瞬間锣险,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工览闰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留芯肤,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓压鉴,卻偏偏與公主長得像崖咨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子油吭,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355

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