03 | 配置準(zhǔn)備:如何搭建多環(huán)境支持玛臂,為 App 開發(fā)作準(zhǔn)備

在開始之前,我先問(wèn)你幾個(gè)問(wèn)題蚕断,在測(cè)試的時(shí)候,App 一般需要連接測(cè)試服務(wù)器入挣,那么在上架后亿乳,還需要連生產(chǎn)服務(wù)器嗎?在發(fā)布前径筏,你的 App 需要通過(guò) Ad-hoc 分發(fā)給內(nèi)部測(cè)試組嗎葛假?在發(fā)布到 App Store 的時(shí)候,你的 App 需要同時(shí)支持免費(fèi)版和收費(fèi)版嗎滋恬?

如果你的回答是“是”聊训,那么你的 App 就需要搭建多環(huán)境支持,優(yōu)化開發(fā)的工作流程恢氯。多環(huán)境提供很多好處带斑,比如能基于同一套源代碼自動(dòng)構(gòu)建出有差異功能的 App鼓寺;能支持多個(gè)團(tuán)隊(duì)并行開發(fā),也能分離測(cè)試和生產(chǎn)環(huán)境勋磕,提高產(chǎn)品的迭代速度妈候,保證上架的 App 通過(guò)嚴(yán)格測(cè)試和功能驗(yàn)證。

在 Moments App 項(xiàng)目中挂滓,我們就使用了三個(gè)不同的環(huán)境苦银,分別是開發(fā)環(huán)境,測(cè)試環(huán)境和生產(chǎn)環(huán)境赶站。它們到底有什么區(qū)別呢墓毒?

  • 開發(fā)環(huán)境, 用于日常的開發(fā)亲怠,一般有未完成的功能模塊所计。編譯時(shí),也不進(jìn)行任何優(yōu)化团秽,可以打印更多的日志主胧,幫助開發(fā)者快速定位問(wèn)題。

  • 測(cè)試環(huán)境习勤, 主要是用于測(cè)試踪栋,以及為產(chǎn)品經(jīng)理進(jìn)行功能驗(yàn)證,包括部分完成的功能模塊图毕,也提供一些隱藏功能夷都,方便我們進(jìn)行開發(fā)和迭代,例如快速切換用戶予颤,清理 Cache囤官,連接到不同后臺(tái)服務(wù)器等等。

  • 生產(chǎn)環(huán)境蛤虐, 只包含通過(guò)了測(cè)試并驗(yàn)證過(guò)的功能模塊党饮,它是最終提交到 App Store 供終端用戶使用的版本。

多環(huán)境支持需要用到 Xcode 的構(gòu)建配置驳庭,這一講刑顺,我就結(jié)合 Moments App 項(xiàng)目來(lái)聊聊這個(gè)問(wèn)題。

Xcode 構(gòu)建基礎(chǔ)概念

一般在構(gòu)建一個(gè) iOS App 的時(shí)候饲常,需要用到 Xcode Project蹲堂,Xcode Target,Build Settings贝淤,Build Configuration 和 Xcode Scheme 等構(gòu)建配置柒竞。它們各有什么用呢?

Xcode Project

Xcode Project用于組織源代碼文件和資源文件霹娄。一個(gè) Project 可以包含多個(gè) Target能犯,例如當(dāng)我們新建一個(gè) Xcode Project 的時(shí)候鲫骗,它會(huì)自動(dòng)生成 App 的主 Target,Unit Test Target 和 UI Test Target踩晶。

在 Moments App 項(xiàng)目中执泰,主 Target 就是 Moments,Unit Test Target 是 MomentsTests渡蜻, UI Test Target 就是 MomentsUITests术吝。

截屏2021-03-12 下午5.01.32.png

Xcode Target

Xcode Target用來(lái)定義如何構(gòu)建出一個(gè)產(chǎn)品(例如 App, Extension 或者 Framework)茸苇,Target 可以指定需要編譯的源代碼文件和需要打包的資源文件排苍,以及構(gòu)建過(guò)程中的步驟。

例如在我們的 Moments App 項(xiàng)目中学密,負(fù)責(zé)單元測(cè)試的MomentsTestsTarget 就指定了 14 個(gè)測(cè)試文件需要構(gòu)建(見下圖的 Compile Sources)淘衙,并且該 Target 依賴了主 App TargetMoments(見下圖的 Dependencies)。

截屏2021-03-12 下午5.03.56.png

有了 Target 的定義腻暮,構(gòu)建系統(tǒng)就可以讀取相關(guān)的源代碼文件進(jìn)行編譯彤守,然后把相關(guān)的資源文件進(jìn)行打包,并嚴(yán)格按照 Target 所指定的設(shè)置和步驟執(zhí)行哭靖。那么 Target 所指定的設(shè)置哪里來(lái)的呢具垫?來(lái)自 Build Settings。

Build Settings

Build Setting保存了構(gòu)建過(guò)程中需要用到的信息试幽,它以一個(gè)變量的形式而存在筝蚕,例如所支持的設(shè)備平臺(tái),或者支持操作系統(tǒng)的最低版本等铺坞。

通常起宽,一條 Build Setting 信息由兩部分組成:名字和值。比如下面是一條 Setting 信息康震,iOS Development Target是名字燎含,而iOS 14.0是值。

截屏2021-03-12 下午5.05.31.png

有了這些基礎(chǔ)知識(shí)以后腿短,接下來(lái)我就結(jié)合 Moments App 來(lái)和你介紹下如何進(jìn)行多環(huán)境配置,從而生成不同環(huán)境版本的 App绘梦。

Moments App 構(gòu)建配置

一般用 Xcode 編譯出不同環(huán)境版本的 App 有多種辦法橘忱,例如拷貝復(fù)制所有源代碼,建立多個(gè) Target 來(lái)包含不同的源碼文件等等卸奉。不過(guò)钝诚,在這里我推薦使用 Build Configuration 和 Xcode Scheme 來(lái)管理多環(huán)境,進(jìn)而構(gòu)建出不同環(huán)境版本的 App榄棵。為什么凝颇?因?yàn)檫@兩個(gè)是目前管理成本最低的辦法潘拱。接下來(lái)我一一介紹下。

Build Configuration

當(dāng)我們?cè)?Xcode 上新建一個(gè)項(xiàng)目的時(shí)候拧略,Xcode 會(huì)自動(dòng)生成兩個(gè) Configuration:Debug和Release芦岂。Debug 用于日常的本地開發(fā),Release 用于構(gòu)建和分發(fā) App垫蛆。而在我們的 Moments App 項(xiàng)目中禽最,有三個(gè) configuration:Debug,Internal 和 AppStore袱饭。它們分別用于構(gòu)建開發(fā)環(huán)境川无、測(cè)試環(huán)境和生產(chǎn)環(huán)境。 其中 Internal 和 AppStore 是從自動(dòng)生成的 Release 拷貝而來(lái)的虑乖。

截屏2021-03-12 下午5.06.16.png

那什么是 Build Configuration 呢懦趋?

Build Configuration就是一組 Build Setting。 我們可以通過(guò) Build Configuration 來(lái)分組和管理不同組合的 Build Setting 集合疹味,然后傳遞給 Xcode 構(gòu)建系統(tǒng)進(jìn)行編譯愕够。

有了 Build Configuration 以后,我們就能為同一個(gè) Build Setting 設(shè)置不同的值佛猛。例如Build Active Architecture Only在 Debug configuration 是Yes惑芭,而在 Internal 和 AppStore configuration 則是No。這樣就能做到同一份源代碼通過(guò)使用不同的 Build Configuration 來(lái)構(gòu)建出功能不一樣的 App 了继找。

截屏2021-03-12 下午5.07.26.png

那么遂跟,在構(gòu)建過(guò)程中怎樣才能選擇不同的 Build Configuration 呢?答案是使用 Xcode Scheme婴渡。

Xcode Scheme

Xcode Scheme用于定義一個(gè)完整的構(gòu)建過(guò)程幻锁,其包括指定哪些 Target 需要進(jìn)行構(gòu)建,構(gòu)建過(guò)程中使用了哪個(gè) Build Configuration 边臼,以及需要執(zhí)行哪些測(cè)試案例等等哄尔。在項(xiàng)目新建的時(shí)候只有一個(gè) Scheme,但可以為同一個(gè)項(xiàng)目建立多個(gè) Scheme柠并。不過(guò)這么多 Scheme 中岭接,同一時(shí)刻只能有一個(gè) Scheme 生效。

我們一起看一下 Moments App 項(xiàng)目的 Scheme 吧臼予。 Moments App 項(xiàng)目有三個(gè) Scheme 來(lái)分別代表三個(gè)環(huán)境鸣戴,Moments Scheme 用于開發(fā)環(huán)境,Moments-Internal Scheme 用于測(cè)試環(huán)境粘拾,而 Moments-AppStore Scheme 用于生產(chǎn)環(huán)境窄锅。

截屏2021-03-12 下午5.07.48.png

下面是MomentsScheme 的配置。


截屏2021-03-12 下午5.08.34.png

左邊是該 Scheme 的各個(gè)操作缰雇,如當(dāng)前選擇了 Build 操作入偷;右邊是對(duì)應(yīng)該操作的配置追驴,比如 Build 對(duì)應(yīng)的 Scheme 可以構(gòu)建三個(gè)不同的 Targets。不同的 Scheme 所構(gòu)建的 Target 數(shù)量可以不一樣疏之,例如下面是Moments-InternalScheme 的配置殿雪。

截屏2021-03-12 下午5.09.00.png

該 Scheme 只構(gòu)建主 App TargetMoments,而不能構(gòu)建其他兩個(gè)測(cè)試 Target体捏。

當(dāng)我們選擇 Run冠摄、Test、Profile几缭、 Analyze 和 Archive 等操作時(shí)河泳,在右欄有一個(gè)很關(guān)鍵的配置是叫作 Build Configuration,我們可以通過(guò)下拉框來(lái)選擇 Moments App 項(xiàng)目里面三個(gè) Configuration (Debug年栓,Internal 和 AppStore) 中的其中一個(gè)拆挥。

截屏2021-03-12 下午5.09.45.png

為了方便管理,我們通常的做法是某抓,一個(gè) Scheme 對(duì)應(yīng)一個(gè) Configuration纸兔。有了這三個(gè) Scheme 以后,我們就可以很方便地構(gòu)建出 Moments α(開發(fā)環(huán)境)否副,Moments β(測(cè)試環(huán)境)和 Moments(生產(chǎn)環(huán)境)三個(gè)功能差異的 App汉矿。

截屏2021-03-12 下午5.10.14.png

你可能已經(jīng)注意到這三個(gè) App 的名字都不一樣,怎么做到的呢备禀?實(shí)際上是我們?yōu)椴煌?Configuration 設(shè)置了不一樣的 Build Setting洲拇。其中決定 App 名字的 Build Setting 叫作PRODUCT_BUNDLE_NAME,然后在 Info.plist 文件里面為 Bundle name 賦值曲尸,就能構(gòu)建出名字不一樣的 App赋续。

截屏2021-03-12 下午5.10.57.png

為了構(gòu)建出不同環(huán)境版本的 App,我們需要經(jīng)常為各個(gè) Build Configuration 下的 Build Setting 設(shè)置不一樣的值另患。 在這其中纽乱,使用好 xcconfig 配置文件就顯得非常重要。

xcconfig 配置文件

xcconfig 會(huì)起到什么作用呢昆箕?

一般修改 Build Setting 的辦法是在 Xcode 的 Build Settings 界面上進(jìn)行鸦列。 例如下面的例子中修改 Suppress Warnings。

截屏2021-03-12 下午5.11.53.png

這樣做有一些不好的地方为严,首先是手工修改很容易出錯(cuò)敛熬,例如有時(shí)候很難看出來(lái)修改的 Setting 到底是 Project 級(jí)別的還是 Target 級(jí)別的。其次第股,最關(guān)鍵的是每次修改完畢以后都會(huì)修改了 xcodeproj 項(xiàng)目文檔 (如下圖所示),導(dǎo)致 Git 歷史很難查看和對(duì)比话原。


截屏2021-03-12 下午5.12.21.png

幸運(yùn)的是夕吻,Xcode 為我們提供了一個(gè)統(tǒng)一管理這些 Build Setting 的便利方法诲锹,那就是使用 xcconfig 配置文件來(lái)管理。

xcconfig 概念及其作用

xcconfig也叫作 Build configuration file(構(gòu)建配置文件)涉馅,我們可以使用它來(lái)為 Project 或 Target 定義一組 Build Setting归园。由于它是一個(gè)純文本文件,我們可以使用 Xcode 以外的其他文本編輯器來(lái)修改稚矿,而且可以保存到 Git 進(jìn)行統(tǒng)一管理庸诱。 這樣遠(yuǎn)比我們?cè)?Xcode 的 Build Settings 界面上手工修改要方便很多,而且還不容易出錯(cuò)晤揣。

在 xcconfig 文件里面的每一條 Setting 都是下面的格式:
BUILD_SETTING_NAME = value
其中桥爽,BUILD_SETTING_NAME表示 Build Setting 的名字,而value是該 Setting 的值昧识。下面是一個(gè)例子钠四。
SWIFT_VERSION = 5.0
SWIFT_VERSION是用于定義 Swift 語(yǔ)言版本的 Build Setting,其值是5.0跪楞。Setting 的名字都是由大寫字母缀去,數(shù)值和下劃線組成。這種命名法我們一般成為蛇型命名法甸祭,例如SNAKE_CASE_NAME缕碎。

當(dāng)我們使用 xcconfig 時(shí),Xcode 構(gòu)建系統(tǒng)會(huì)按照下面的優(yōu)先級(jí)來(lái)計(jì)算出 Build Setting 的最后生效值

  • Platform Defaults (平臺(tái)默認(rèn)值)

  • Xcode Project xcconfig File(Project 級(jí)別的 xcconfig 文件)

  • Xcode Project File Build Settings(Project 級(jí)別的手工配置的 Build Setting)

  • Target xcconfig File (Target 級(jí)別的 xcconfig 文件)

  • Target Build Settings(Target 級(jí)別的手工配置的 Build Setting)

Xcode 構(gòu)建系統(tǒng)會(huì)按照上述列表從上而下讀取 Build Setting池户,如果發(fā)現(xiàn)同樣的 Setting 咏雌,就會(huì)把下面的 Setting 覆蓋掉上面的,越往下優(yōu)先級(jí)別越高煞檩。

例如我們?cè)?Project 級(jí)別的 xcconfig 文件配置了SWIFT_VERSION = 5.0而在Target 級(jí)別的 xcconfig 文件配置了SWIFT_VERSION = 5.1处嫌,那么Target 級(jí)別的 Build Setting 會(huì)覆蓋 Project 級(jí)別的SWIFT_VERSION設(shè)置,最終SWIFT_VERSION生效的值是5.1斟湃。

那么熏迹,要怎樣做才能做到不覆蓋原有的 Build Setting 呢?我們可以使用下面例子中的$(inherited)來(lái)實(shí)現(xiàn)凝赛。
BUILD_SETTING_NAME = $(inherited) additional value
可以保留原先的 Setting注暗,然后把新的值添加到后面去。比如:
FRAMEWORK_SEARCH_PATHS = $(inherited) ./Moments/Pods
其中的FRAMEWORK_SEARCH_PATHS會(huì)保留原有的值墓猎,然后加上./Moments/Pods作為新值捆昏。
在配置 Build Setting 時(shí),還可以引用其他已定義的 Build Setting毙沾。

例如下面的例子中骗卜,FRAMEWORK_SEARCH_PATHS使用了另外一個(gè) Build SettingPROJECT_DIR
FRAMEWORK_SEARCH_PATHS = $(inherited) $(PROJECT_DIR)
為了重用,我們可以通過(guò)#include引入其他 xcconfig 文件寇仓。
#include "path/to/OtherFile.xcconfig"
Moments App xcconfig 配置文件
下面我們一起來(lái)看看 Moments App 項(xiàng)目是怎樣管理 xcconfig 配置文件吧举户。

截屏2021-03-12 下午5.20.00.png

我們把所有 xcconfig 文件分成三大類:Shared、 Project 和 Targets遍烦。

其中 Shared 文件夾用于保存分享到整個(gè) App 的 Build Setting俭嘁,例如 Swift 的版本號(hào)、App 所支持的 iOS 版本號(hào)等各種共享的基礎(chǔ)信息服猪。 下面是 SDKAndDeviceSupport.xcconfig 文件里面所包含的信息:

TARGETED_DEVICE_FAMILY = 1
IPHONEOS_DEPLOYMENT_TARGET = 14.0

TARGETED_DEVICE_FAMILY表示支持的設(shè)備供填,1表示 iPhone。而IPHONEOS_DEPLOYMENT_TARGET表示支持 iOS 的最低版本罢猪,我們的 Moments App 所支持的最低版本是 iOS 14.0近她。

Project 文件夾用于保存 Xcode Project 級(jí)別的 Build Setting,其中 BaseProject.xcconfig 會(huì)引入 Shared 文件夾下所有的 xcconfig 配置文件坡脐,如下所示:

#include "CompilerAndLanguage.xcconfig"
#include "SDKAndDeviceSupport.xcconfig"
#include "BaseConfigurations.xcconfig"

然后我們會(huì)根據(jù)三個(gè)不同的環(huán)境分別建了三個(gè)xcconfig 配置文件泄私,如下:

  • DebugProject.xcconfig 文件
#include "BaseProject.xcconfig"
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) DEBUG
  • InternalProject.xcconfig 文件
#include "BaseProject.xcconfig"
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) INTERNAL
  • AppStoreProject.xcconfig 文件
#include "BaseProject.xcconfig"
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) PRODUCTION

它們的共同點(diǎn)是都引入了用于共享的 BaseProject.xcconfig 文件,然后分別定義了 Swift 編譯條件配置SWIFT_ACTIVE_COMPILATION_CONDITIONS备闲。其中$(inherited)表示繼承原有的配置晌端,$(inherited)后面的DEBUG或者INTERNAL表示在原有配置的基礎(chǔ)上后面添加了一個(gè)新條件。有了這些編譯條件恬砂,我們就可以在代碼中這樣使用:

#if DEBUG
    print("Debug Environment")
#endif

該段代碼只在開發(fā)環(huán)境執(zhí)行咧纠,因?yàn)橹挥虚_發(fā)環(huán)境的SWIFT_ACTIVE_COMPILATION_CONDITIONS才有DEBUG的定義。這樣做能有效分離各個(gè)環(huán)境泻骤,保證同一份代碼構(gòu)建出對(duì)應(yīng)不同環(huán)境的 App漆羔。

Targets 文件夾用于保存 Xcode Target 級(jí)別的 Build Setting,也是由一個(gè) BaseTarget.xcconfig 文件來(lái)共享所有 Target 都需要使用的信息狱掂。

PRODUCT_BUNDLE_NAME = Moments

這里的PRODUCT_BUNDLE_NAME是 App 的名字演痒。
下面是三個(gè)不同環(huán)境的 Target xcconfig 文件。

  • DebugTarget.xcconfig
#include "../Pods/Target Support Files/Pods-Moments/Pods-Moments.debug.xcconfig"
#include "BaseTarget.xcconfig"
PRODUCT_BUNDLE_NAME = $(inherited) α
PRODUCT_BUNDLE_IDENTIFIER = com.ibanimatable.moments.development
  • InternalTarget.xcconfig
#include "../Pods/Target Support Files/Pods-Moments/Pods-Moments.internal.xcconfig"

#include "BaseTarget.xcconfig"

PRODUCT_BUNDLE_NAME = $(inherited) β

PRODUCT_BUNDLE_IDENTIFIER = com.ibanimatable.moments.internal

  • AppStoreTarget.xcconfig
#include "../Pods/Target Support Files/Pods-Moments/Pods-Moments.appstore.xcconfig"
#include "BaseTarget.xcconfig"
PRODUCT_BUNDLE_NAME = $(inherited)
PRODUCT_BUNDLE_IDENTIFIER = com.ibanimatable.moments

它們都需要引入 CocoaPods 所生成的 xcconfig 和共享的 BaseTarget.xcconfig 文件趋惨,然后根據(jù)需要改寫 App 的名字鸟顺。例如DebugTarget 覆蓋了PRODUCT_BUNDLE_NAME的值為Moments α*, 其所構(gòu)建的 App 叫Moments α*。

一般在 App Store 上所有 App 的標(biāo)識(shí)符都必須是唯一的器虾。如果你的項(xiàng)目通過(guò) Configuration 和 Scheme 來(lái)生成免費(fèi)版和收費(fèi)版的 App讯嫂,那么,你必須在兩個(gè) Configuration 中分別為PRODUCT_BUNDLE_IDENTIFIER配置對(duì)應(yīng)的標(biāo)識(shí)符兆沙,例如com.lagou.freecom.lagou.paid欧芽。

在 Moments App 中,我們也為各個(gè)環(huán)境下的 App 使用了不同的標(biāo)識(shí)符葛圃,以方便我們通過(guò) CI 自動(dòng)構(gòu)建千扔,并分發(fā)到內(nèi)部測(cè)試組或者 App Store憎妙。同時(shí),這也能為各個(gè)環(huán)境版本的 App 分離用戶行為數(shù)據(jù)昏鹃,方便統(tǒng)計(jì)分析尚氛。

一旦有了這些 xcconfig 配置文件诀诊,今后我們就可以在 Xcode 的 Project Info 頁(yè)面里的 Configurations 上引用它們洞渤。

截屏2021-03-12 下午5.32.07.png

下面是所有 Configurations 所引用的 xcconfig 文件。

截屏2021-03-12 下午5.32.40.png

在配置好所有 xcconfig 文件的引用以后属瓣,可以在 Build Settings 頁(yè)面查看某個(gè) Build Setting 的生效值载迄。我們以IPHONEOS_DEPLOYMENT_TARGET為例,一起看看抡蛙。

截屏2021-03-12 下午5.33.20.png

當(dāng)我們選擇AllLevels時(shí)护昧,可以看到所有配置信息分成了不同的列。這些列分別代表前面的 Build Settng 優(yōu)先級(jí):

  • 平臺(tái)默認(rèn)值

  • Project 級(jí)別的 xcconfig 文件

  • Xcode 項(xiàng)目文件中的 Project 級(jí)別配置

  • Target 級(jí)別的 xcconfig 文件

  • Xcode 項(xiàng)目文件中的 Target 級(jí)別配置

Build Settng 的優(yōu)先級(jí)是從左到右排序的粗截。越是左邊優(yōu)先級(jí)就越高惋耙。例如,我們?cè)?Project 級(jí)別的 xcconfig 文件里面定義了IPHONEOS_DEPLOYMENT_TARGET的值為14.0熊昌,那么Project 級(jí)別的 xcconfig 文件(Project Config File) 一列上就會(huì)顯示iOS 14.0绽榛,它覆蓋了系統(tǒng)的默認(rèn)值 (iOS Default)iOS 14.2。這就是因?yàn)?Project 級(jí)別的 xcconfig 文件婿屹,它的優(yōu)先級(jí)高于系統(tǒng)默認(rèn)值灭美,因此最后生效的值是iOS 14.0

總結(jié)

介紹了如何通過(guò) Build Configuration昂利、 Xcode Scheme 以及 xcconfig 配置文件來(lái)統(tǒng)一項(xiàng)目的構(gòu)建配置届腐,從而搭建出多個(gè)不同環(huán)境,為后期構(gòu)建出對(duì)應(yīng)環(huán)境的 App 做準(zhǔn)備蜂奸。

截屏2021-03-12 下午5.35.45.png

在使用 xcconfig 配置時(shí)犁苏,還是需要注意以下兩點(diǎn):

首先,我們必須把所有 Build Setting 都配置在 xcconfig 文件里面扩所,并通過(guò) Git 進(jìn)行統(tǒng)一管理围详;

其次,我們千萬(wàn)不要在 Xcode 的 Build Settings 頁(yè)面修改任何 Setting碌奉,否則該配置會(huì)覆蓋 xcconfig 文件里面的配置短曾。如果你不小心修改了,可以通過(guò)點(diǎn)擊刪除鍵把頁(yè)面是的配置刪掉赐劣。
思考題:
我們 Moments App 項(xiàng)目的主 App 為什么只使用了一個(gè) Target 嗎嫉拐?如果使用多個(gè) Target,例如 Debug Target魁兼,Internal Target 和 Release Target 會(huì)有什么問(wèn)題婉徘?

源碼地址:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末漠嵌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子盖呼,更是在濱河造成了極大的恐慌儒鹿,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件几晤,死亡現(xiàn)場(chǎng)離奇詭異约炎,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蟹瘾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門圾浅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人憾朴,你說(shuō)我怎么就攤上這事狸捕“は拢” “怎么了谜喊?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵就乓,是天一觀的道長(zhǎng)佛吓。 經(jīng)常有香客問(wèn)我茂洒,道長(zhǎng)把跨,這世上最難降的妖魔是什么鸳劳? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任肿男,我火速辦了婚禮纯蛾,結(jié)果婚禮上纤房,老公的妹妹穿的比我還像新娘。我一直安慰自己翻诉,他們只是感情好炮姨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著碰煌,像睡著了一般舒岸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上芦圾,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天蛾派,我揣著相機(jī)與錄音,去河邊找鬼个少。 笑死洪乍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的夜焦。 我是一名探鬼主播壳澳,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼茫经!你這毒婦竟也來(lái)了巷波?” 一聲冷哼從身側(cè)響起萎津,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎抹镊,沒(méi)想到半個(gè)月后锉屈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡垮耳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年颈渊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氨菇。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡儡炼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出查蓉,到底是詐尸還是另有隱情,我是刑警寧澤榜贴,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布豌研,位于F島的核電站,受9級(jí)特大地震影響唬党,放射性物質(zhì)發(fā)生泄漏鹃共。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一驶拱、第九天 我趴在偏房一處隱蔽的房頂上張望霜浴。 院中可真熱鬧,春花似錦蓝纲、人聲如沸阴孟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)永丝。三九已至,卻和暖如春箭养,著一層夾襖步出監(jiān)牢的瞬間慕嚷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工毕泌, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留喝检,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓撼泛,卻偏偏與公主長(zhǎng)得像挠说,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子坎弯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

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