iOS 靜態(tài)代碼分析(SonarQube + Objective-C嘲叔、Swift)

前言

??? 首次接觸代碼分析,源于公司對(duì)迭代規(guī)范流程的的試點(diǎn)浑塞。在迭代中,如何保證輸出的代碼是高質(zhì)量的政己,或者說是沒有明顯缺陷的酌壕,這種高質(zhì)如何量化,這就是接下來要介紹的: 靜態(tài)代碼分析歇由。

代碼分析的意義

降低風(fēng)險(xiǎn)

??? 迭代中代碼掃描分析卵牍,成為上線發(fā)版不可或缺的一環(huán),代碼分析能檢測(cè)出代碼中明顯存在的問題沦泌,降低風(fēng)險(xiǎn)糊昙,比如:強(qiáng)制類型轉(zhuǎn)換、內(nèi)存泄漏谢谦、空指針……

提高代碼質(zhì)量

??? 代碼的質(zhì)量水平释牺,是直接影響系統(tǒng)穩(wěn)定程度的原因之一,Code Review分為工具回挽、人工兩種没咙,通過Code Review,可以量化代碼質(zhì)量千劈,是否達(dá)到發(fā)版的質(zhì)量標(biāo)準(zhǔn)祭刚,這里重點(diǎn)講工具Code Review。

掃描環(huán)境搭建

1. 環(huán)境

  • 版本控制工具:Git
  • 源碼語言版本:Swift 5墙牌、OC
  • 開發(fā)工具:Xcode 11.3
  • JDK:Jdk-1.8.0_221 JDK1.8地址
  • 持續(xù)集成工具:Jenkins
  • 質(zhì)量管理工具:SonarQube(7.8 )

2. SonarQube的工具鏈

  • 源碼工程(Project):Swift涡驮、OC
  • MySQL數(shù)據(jù)庫(SonarQube Database):版本5.7.19-macos10.12-x86_64,存放配置信息和分析結(jié)果信息
    創(chuàng)建用戶及數(shù)據(jù)庫喜滨,mysql修改密碼自行操作捉捅,隨意12345678
cd /usr/local/mysql/
source ~/.bash_profile
mysql -u root -p
mysql> CREATE DATABASE sonar CHARACTER SET utf8 COLLATE utf8_general_ci;
mysql> CREATE USER 'sonar' IDENTIFIED BY 'sonar';
mysql> GRANT ALL ON sonar.* TO 'sonar'@'%' IDENTIFIED BY 'sonar';
mysql> GRANT ALL ON sonar.* TO 'sonar'@'localhost' IDENTIFIED BY 'sonar';
mysql> FLUSH PRIVILEGES;
mysql> quit;
  • 服務(wù)器(SonarQube Server):iMac ,發(fā)布應(yīng)用虽风,在線瀏覽锯梁、配置分析即碗;
  • 客戶端(SonarQube Scanner):版本4.2.0.1873,執(zhí)行源代碼分析陌凳。
注:Sonarqube-7.8 社區(qū)版 7.9版本以下支持MySQL(>=5.6<8)

3. 運(yùn)行SonarQube

  • 解壓sonarqube-7.8.zip
  • 修改sonarqube-7.8中conf文件夾下的sonar.properties
sonar.jdbc.url=jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance&useSSL=false
# mysql中創(chuàng)建的sonarQube用戶
sonar.jdbc.username=root
# 創(chuàng)建用戶對(duì)應(yīng)的密碼
sonar.jdbc.password=12345678
# 設(shè)置編碼格式為UTF-8
sonar.sorceEncoding=UTF-8
# sonar登陸用戶名
sonar.login=admin
# sonar登陸密碼
sonar.password=admin
sonar安裝成功
image.png
  • 開啟SonarQube訪問外部訪問剥懒,修改sonarqube-7.8中conf文件夾下的sonar.properties文件,注釋掉sonar.web.context=
1590831762831_E450B976-90EE-4ED1-9345-F9AF77C1BCE5.png
  • 開啟apache httpd隨機(jī)啟動(dòng)合敦,提供外部訪問
sudo launchctl load -w /System/Library/LaunchDaemons/org.apache.httpd.plist
  • Mac系統(tǒng)升級(jí)之后初橘,啟動(dòng)Sonar服務(wù)部不會(huì)自送啟動(dòng)MySQL,導(dǎo)致Sonar服務(wù)啟動(dòng)失敗充岛,需要在開機(jī)啟動(dòng)項(xiàng)中增加MySQL啟動(dòng)配置保檐。
sh /usr/local/mysql-5.7.19-macos10.12-x86_64/support-files/mysql.server start
  • 配置sonar開機(jī)自啟,在/Users/xxx/Document下創(chuàng)建Config文件夾崔梗,在Config下創(chuàng)建startup.sh文件夜只,添加以下腳本
# MySQL開機(jī)啟動(dòng)
sh /usr/local/mysql-5.7.19-macos10.12-x86_64/support-files/mysql.server start

# Sonar開機(jī)啟動(dòng)
sh /usr/local/sonarqube-7.8/bin/macosx-universal-64/sonar.sh start

# Jenkins開機(jī)自啟 并做外網(wǎng)端口映射
java -jar /usr/local/Cellar/jenkins/2.271/libexec/jenkins.war --httpPort=8080
  • 將run sonar.sh文件添加到iMac登錄項(xiàng)中


    image.png

自此SonarQube服務(wù)開機(jī)自啟動(dòng)、外部訪問配置完畢


image.png

4. 安裝插件

5. 插件配置

  • 從已下載的sonar-swift-0.4.6中蒜魄,拷貝backelite-sonar-swift-plugin-0.4.6.jar文件到sonarqube-7.8/extensions/plugins里

  • 從已下載的漢化插件sonar-l10n-zh-plugin-1.28中扔亥,拷貝sonar-l10n-zh-plugin-1.28文件到sonarqube-7.8/extensions/plugins里

  • 下載run-sonar-swift.sh文夾拷貝到swift項(xiàng)目的根目錄下(與projext.xcodeproj 同級(jí))

  • 下載sonar-project.properties文件拷貝到swift項(xiàng)目的根目錄下(與projext.xcodeproj 同級(jí))

image.png
  • 編輯sonar-project.properties文件,根據(jù)swift項(xiàng)目修改其中的參數(shù)
#
# Swift SonarQube Plugin - Enables analysis of Swift and Objective-C projects into SonarQube.
# Copyright ? 2015 Backelite (${email})
#

##########################
# Required configuration #
##########################

# 項(xiàng)目key, 與sonar中項(xiàng)目key一致
sonar.projectKey=xxx

# 項(xiàng)目 name
sonar.projectName=xxx

# 版本號(hào)
sonar.projectVersion=2.3.0

# 分支
sonar.branch.name=XXX

# Encoding of the source code
sonar.sourceEncoding=UTF-8

# 語言 ObjC / Swift
sonar.language=swift

# swift 源文件路徑
sonar.sources=Project/Clasess

# 過濾文件谈为、目錄
sonar.test.inclusions=**/*Test*/**
sonar.test.inclusions=*.swift
sonar.exclusions=Project/Clasess/Vendors/**/*

# 項(xiàng)目描述
sonar.projectDescription=XXX ios with the SonarScanner

# 測(cè)試目錄的路徑
sonar.tests=ProjectTests,ProjectUITests

# 模擬器配置
sonar.swift.simulator=platform=iOS Simulator,name=iPhone X,OS=latest

# scheme Configuration Debug/Release
sonar.swift.appConfiguration=Debug

# Xcode 工程文件配置 xcodeproj旅挤、xcworkspace
sonar.swift.project=Project.xcodeproj
sonar.swift.workspace=Project.xcworkspace

# Xcode Scheme
sonar.swift.appScheme=Project

# App 名稱
sonar.swift.appName=Project

##########################
# Optional configuration #
##########################

# SCM
sonar.scm.enabled=true
# sonar.scm.url=scm:git:http://xxx

# JUnit report generated
sonar.junit.reportsPath=sonar-reports/

# Lizard report generated
sonar.swift.lizard.report=sonar-reports/lizard-report.xml

# Coverage report generated
sonar.swift.coverage.reportPattern=sonar-reports/coverage-swift*.xml

# OCLint report generated
sonar.objectivec.oclint.report=sonar-reports/*oclint.txt

# Swiftlint report generated
sonar.swift.swiftlint.report=sonar-reports/*swiftlint.txt

# Paths to exclude from coverage report (surefire, 3rd party libraries etc.)
# sonar.swift.excludedPathsFromCoverage=pattern1,pattern2
sonar.swift.excludedPathsFromCoverage=.*Tests.*

# Tailor report generated
sonar.swift.tailor.config=--no-color --max-line-length=100 --max-file-length=500 --max-name-length=40 --max-name-length=40 --min-name-length=4
sonar.swift.tailor.report=sonar-reports/*tailor.txt

# login info
sonar.host.url=http://localhost:9000
sonar.login=admin
sonar.password=xxx

# database info
sonar.jdbc.username=root
sonar.jdbc.password=xxx
sonar.jdbc.url=jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance&useSSL=false

???

  • run-sonar-swift.shsonar-project.properties如果不想以手動(dòng)的形式放入工程目錄下,可將run-sonar-swift.sh放在服務(wù)器某個(gè)目錄下伞鲫,將sonar-project.properties配置寫入Jenkins構(gòu)建腳本中粘茄,在執(zhí)行Jenkins構(gòu)建時(shí)通過腳本將run-sonar-swift.shcopy到工程根目錄下,將sonar-project.properties寫入工程根目錄下秕脓。這樣即使多個(gè)項(xiàng)目柒瓣,也不用單獨(dú)修改項(xiàng)目工程,僅修改腳本即可吠架。掃描OC項(xiàng)目的Jenkins腳本如下:
source /Users/melo/.bash_profile
export LANG="en_US.UTF-8"  
export LANGUAGE="en_US.UTF-8"  
export LC_ALL="en_US.UTF-8"  

cd $WORKSPACE
  
pod install --verbose --no-repo-update 

rm -f run-sonar.sh
rm -f sonar-project.properties

cat>sonar-project.properties<<EOF
##########################
# Required configuration #
##########################

# sonar平臺(tái)中相對(duì)應(yīng)項(xiàng)目的key
sonar.projectKey=XXX

# sonar平臺(tái)中相對(duì)應(yīng)項(xiàng)目的名字
sonar.projectName=XXX

# 版本號(hào)
sonar.projectVersion=6.0.0

# Encoding of the source code
sonar.sourceEncoding=UTF-8

# 語言 ObjC / Swift
sonar.language=ObjC

# sonar檢測(cè)的源文件目錄嘹朗,‘.’表示當(dāng)前根目錄下的所有文件目錄;包含主要源文件的目錄的逗號(hào)分隔路徑
sonar.sources=.

# 檢測(cè)中的測(cè)試源文件(指定單元測(cè)試文件)
# sonar.test.inclusions=**/*Test*/**
# sonar.test.inclusions=*.swift,*.m

# 檢測(cè)中排除的源文件(排除的源文件不參與檢測(cè),一般排除單元測(cè)試文件诵肛、配置文件等)
# sonar.exclusions=XXX1,XXX2,XXX3……

# 項(xiàng)目描述
sonar.projectDescription=XXX iOS with the SonarScanner

# sonar檢測(cè)的測(cè)試文件目錄屹培,‘.’表示當(dāng)前根目錄下的所有文件目錄;包含測(cè)試源文件的目錄的逗號(hào)分隔路徑。從構(gòu)建系統(tǒng)中讀取Maven怔檩,Gradle褪秀,MSBuild項(xiàng)目。否則默認(rèn)為空薛训。
# sonar.tests=XXXTests,XXXUITests

# 檢測(cè)中排除的測(cè)試源文件(排除的源文件不參與檢測(cè))
# sonar.test.exclusions=

# 模擬器配置
sonar.objectivec.simulator=platform=iOS Simulator,name=iPhone 11 Pro Max,OS=latest

# scheme Configuration Debug/Release
sonar.objectivec.appConfiguration=Debug

# Xcode 工程文件配置 xcodeproj媒吗、xcworkspace
sonar.objectivec.project=XXX.xcodeproj
sonar.objectivec.workspace=XXX.xcworkspace

# Xcode Scheme
sonar.objectivec.appScheme=TuyaSmartPublic

# App 名稱
sonar.objectivec.appName=XXX

##########################
# Optional configuration #
##########################

# SCM
sonar.scm.enabled=true
# sonar.scm.url=scm:git:http://xxx

# JUnit report generated
sonar.junit.reportsPath=sonar-reports/

# Lizard report generated
sonar.objectivec.lizard.report=sonar-reports/lizard-report.xml

# Coverage report generated
sonar.objectivec.coverage.reportPattern=sonar-reports/coverage-objectivec*.xml

# 報(bào)告的名字
sonar.objectivec.oclint.report=oclint.xml

# 報(bào)告的路徑
sonar.objectivec.oclint.reportPath=sonar-reports/oclint.xml

# Swiftlint report generated
#sonar.swift.swiftlint.report=sonar-reports/*swiftlint.txt

# Paths to exclude from coverage report (surefire, 3rd party libraries etc.)
# sonar.swift.excludedPathsFromCoverage=pattern1,pattern2
sonar.objectivec.excludedPathsFromCoverage=.*Tests.*

# Tailor report generated
sonar.objectivec.tailor.config=--no-color --max-line-length=100 --max-file-length=500 --max-name-length=40 --max-name-length=40 --min-name-length=4
sonar.objectivec.tailor.report=sonar-reports/*tailor.txt

sonar.objectivec.coverageType=legacy

# login info
sonar.host.url=http://localhost:9000
sonar.login=XXX
sonar.password=XXX

# database info
sonar.jdbc.username=root
sonar.jdbc.password=12345678
sonar.jdbc.url=jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance&useSSL=false
sonar.verbose=true 
EOF

cp /Users/XXX/Documents/Sonar/run-sonar.sh $WORKSPACE
./run-sonar.sh -v
  • 配置插件規(guī)則,在掃描文件下添加.swiftlint.yml文件乙埃,根據(jù)自身代碼的情況闸英,選擇性的忽略部分規(guī)則锯岖,或者修改swift插件規(guī)則。
image.png
use_nested_configs: true

# 執(zhí)行 linting 時(shí)忽略的路徑甫何。 優(yōu)先級(jí)比 `included` 更高出吹。
excluded:
  - Pods

# 執(zhí)行時(shí)排除掉的規(guī)則
disabled_rules:
  - force_cast # 強(qiáng)制轉(zhuǎn)換
  - force_try # try語句判斷
  - line_length # 單行代碼長度

 #代碼復(fù)雜度
cyclomatic_complexity:
  warning: 19
  error: 20

# 文件長度
file_length:
  warning: 999
  error: 1000

# 實(shí)體類長度
type_body_length:
  warning: 999
  error: 1000
  
# 函數(shù)體長度
function_body_length:
  warning: 99
  error: 100
  
# 命名規(guī)則可以設(shè)置最小長度和最大程度的警告/錯(cuò)誤。此外它們也可以設(shè)置排除在外的名字
type_name:
  min_length: 4 # 只是警告
  max_length: # 警告和錯(cuò)誤
    warning: 40
    error: 50
#  excluded: iPhone # 排除某個(gè)名字
#variable_name:
#  min_length: # 只有最小長度
#    error: 4 # 只有錯(cuò)誤
#  excluded: # 排除某些名字
#    - id
#    - URL
#    - GlobalAPIKey
reporter: "xcode" # 報(bào)告類型 (xcode, json, csv, checkstyle)

???
更多規(guī)則辙喂,可參考

注:.swiftlint.yml 文件必須放在掃描的具體路徑下捶牢,放在工程根目錄下無效。

6. 配置變量配置

open ~/.bash_profile

# jdk
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/ Contents/Home
export CLASSPATH=.:${JAVA_HOME}/lib/dt.jar:${JAVA_HOME}/lib/ tools.jar
#export PATH=${JAVA_HOME}/bin:$PATH

# maven
export M3_HOME=/usr/local/Cellar/maven/3.6.3_1/libexec
export PATH=${JAVA_HOME}/bin:$M3_HOME/bin:$PATH

# gradle 
export GRADLE_HOME=/usr/local/Cellar/gradle/4.1
export PATH=$PATH:$GRADLE_HOME/bin

# mysql
export PATH=${PATH}:/usr/local/mysql/bin
export PATH=$PATH:/usr/local/mysql/support-files

# postgresql
export PATH=${PATH}:/usr/local/Cellar/postgresql/13.1/bin 

# sonarqube
export SONARQUBE=/usr/local/sonarqube-7.8 
export PATH=$SONARQUBE/bin:$PATH

# sonar-scanner
export SONAR_SCANNER=/usr/local/sonar-scanner-4.5.0.2216-macosx 
export PATH=$SONAR_SCANNER/bin:$PATH

# python
PYTHON=/Library/Frameworks/Python.framework/Versions/3.8 
export PATH=$PYTHON/bin:$PATH
alias python="/Library/Frameworks/Python.framework/Versions/3.8/bin/ python3.8"

#PYTHON=/System/Library/Frameworks/Python.framework/Versions/ 2.7
#export PATH=$PYTHON/bin:$PATH
#alias python="/System/Library/Frameworks/Python.framework/Versions/ 2.7/bin/python2.7"

# oclint 
export OCLINT=/usr/local/Cellar/oclint/0.13.1 
export PATH=$OCLINT_HOME/bin:$PATH

# swiftlint
export SWIFTLINT=/usr/local/Cellar/swiftlint/0.41.0
export PATH=$SWIFTLINT/bin:$PATH

# xcpretty
XCPRETTY=/usr/local/bin/xcpretty
export PATH=$XCPRETTY:$PATH

# tailor
export TAILOR=/usr/local/Cellar/tailor/0.12.0_1
export PATH=$TAILOR/bin:$PATH

# lizard
LIZARD=/usr/local/bin/lizard
export PATH=$LIZARD:$PATH

# slather
SLATHER=/usr/local/bin/slather
export PATH=$SLATHER:$PATH

# xctool
export XCTOOL=/usr/local/Cellar/xctool/0.3.7
export PATH=$XCTOOL/bin:$PATH

# gcovr
export GCOVR=/usr/local/Cellar/gcovr/4.2_1
export PATH=$GCOVR/bin:$PATH

# xcodebuild
XCODEBUILD=/usr/bin/xcodebuild
export PATH=$XCODEBUILD:$PATH

# local
export USRLOCAL=/usr/local
export PATH=$USRLOCAL/bin:$PATH

保存環(huán)境配置
source ~/.bashrc

7. 源碼工程配置

Tagrget配置:Tests巍耗、UITests必不可少

image.png

8. Jenkins構(gòu)建掃描

創(chuàng)建Jenkins掃描任務(wù)
image.png
配置源碼
image.png

??? 關(guān)于SonrQube 分析iOS項(xiàng)目的詳細(xì)配置秋麸,可參考SonarQube Integration with iOS

配置Jenkins掃描腳本
source /Users/xxxx/.bash_profile
export LC_ALL="en_US.UTF-8"
cd $WORKSPACE
./run-sonar-swift.sh -v
掃描成功結(jié)果圖
image.png
掃描日志
image.png
SonarQube分析結(jié)果
image.png
image.png
導(dǎo)出報(bào)告

社區(qū)版導(dǎo)出報(bào)告需要安裝開源插件sonar-cnes-report.
這里因?yàn)椴渴鸬氖莝onar7.8的版本,與sonar-cnes-report最新的4.0.0版本不兼容炬太,新增插件后導(dǎo)致sonar重啟失敗灸蟆。選擇一個(gè)較低的版本即可,這里選擇3.3.1版本即可亲族,其他版本也可以嘗試一下炒考。
將下載的 jar包復(fù)制到中sonarqube的plugins目錄下,重新啟動(dòng) sonarqube孽水,然后單擊“更多”>“CNES 報(bào)告”票腰。
注意:由于個(gè)人安裝sonarqube的目錄各不相同城看,這里plugins目錄為:/usr/local/sonarqube-7.8/extensions/plugins女气,選擇自己的目錄即可。

image.png

image.png

image.png

常見錯(cuò)誤:
1测柠、Server接受的數(shù)據(jù)包大小炼鞠,會(huì)受 max_allowed_packet 參數(shù)限制,導(dǎo)致寫入或者更新失敗轰胁。

11:44:53.491 ERROR: Error during SonarScanner execution
Failed to upload report - An error has occurred. Please contact your administrator
+ returnValue=2
+ set +x
ERROR - Command 'sonar-scanner -X -Dsonar.verbose=true' failed with error code: 2

解決方案:修改MySQL的max_allowed_packet的大小谒主。
關(guān)于max_allowed_packet的修改,請(qǐng)參考:MYSQL 5.7 無法修改max_allowed_packet參數(shù)赃阀?

??? 到這里霎肯,SonarQube代碼掃描結(jié)束,接下來就是根據(jù)分析結(jié)果榛斯,做針對(duì)性的整改代碼观游。我們目前的策略把定義為阻斷、嚴(yán)重驮俗、主要問題清零懂缕,次要、提示這兩類問題先不處理王凑,后期有時(shí)間再處理搪柑。中間針對(duì)一些不合理的維度聋丝,比如:循環(huán)復(fù)雜度到達(dá)10定義為嚴(yán)重問題,文件工碾、類代碼行數(shù)限制200行弱睦,超過定義主要問題,單行代碼長度限制120字符等等倚喂,做了插件規(guī)則的調(diào)整:忽略/修改閾值每篷。總之一句話:具體問題具體分析端圈,根據(jù)自家代碼實(shí)際情況做評(píng)估焦读,如果代碼整改需要大刀闊斧,那跟重構(gòu)無異了舱权,厲害關(guān)系需要權(quán)衡清楚矗晃。另外SonarQube問題的整改,盡量放在項(xiàng)目中去做宴倍,迭代周期太短张症,開發(fā)、測(cè)試很難把控到位鸵贬。如果一定要放在迭代中來做俗他,那一定要排好整改計(jì)劃,按節(jié)奏推進(jìn)阔逼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末兆衅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子嗜浮,更是在濱河造成了極大的恐慌羡亩,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件危融,死亡現(xiàn)場(chǎng)離奇詭異畏铆,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)吉殃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門辞居,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蛋勺,你說我怎么就攤上這事瓦灶。” “怎么了迫卢?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵倚搬,是天一觀的道長。 經(jīng)常有香客問我乾蛤,道長每界,這世上最難降的妖魔是什么捅僵? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮眨层,結(jié)果婚禮上庙楚,老公的妹妹穿的比我還像新娘。我一直安慰自己趴樱,他們只是感情好馒闷,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著叁征,像睡著了一般纳账。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上捺疼,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天疏虫,我揣著相機(jī)與錄音,去河邊找鬼啤呼。 笑死卧秘,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的官扣。 我是一名探鬼主播翅敌,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼惕蹄!你這毒婦竟也來了蚯涮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤焊唬,失蹤者是張志新(化名)和其女友劉穎恋昼,沒想到半個(gè)月后看靠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赶促,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年挟炬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鸥滨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谤祖,死狀恐怖婿滓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情粥喜,我是刑警寧澤凸主,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站额湘,受9級(jí)特大地震影響卿吐,放射性物質(zhì)發(fā)生泄漏旁舰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一嗡官、第九天 我趴在偏房一處隱蔽的房頂上張望箭窜。 院中可真熱鬧,春花似錦衍腥、人聲如沸磺樱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽竹捉。三九已至,卻和暖如春尚骄,著一層夾襖步出監(jiān)牢的瞬間活孩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工乖仇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留憾儒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓乃沙,卻偏偏與公主長得像起趾,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子警儒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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