iOS 底層原理38:自動(dòng)化打包(一)Xcode + Shell腳本

iOS 底層原理 文章匯總

Apple提供的常規(guī)打包方式主要是由Xcode支持的逐抑,下面展開來聊聊

Xcode打包

Xcode的打包主要分為兩步:

  • Archive:對(duì)target進(jìn)行編譯褐墅、歸檔绢涡,生成.xcarchive 文件
  • Export:對(duì)生成的.xcarchive 文件進(jìn)行進(jìn)一步的處理抡蛙,生成不同渠道的ipa包卿拴,進(jìn)行分發(fā)

Archive編譯歸檔

Archive主要是對(duì)target進(jìn)行編譯蹈矮、歸檔揪漩,生成.xcarchive 文件“D欤可在Xcode -> Window -> Organizer -> Archives中查看

archive_01.jpg

archive_02

archive_03

這里所說的歸檔赵颅,主要就對(duì)項(xiàng)目源碼進(jìn)行編譯后虽另,再將編譯生成的各種文件暂刘、資源、記錄統(tǒng)一封裝到一個(gè)文件中捂刺,便于管理和回溯谣拣。

隨機(jī)選擇一個(gè).xcarchive文件,點(diǎn)擊show in Finder族展,可以看到一個(gè).xcarchive 后綴的文件


xcarchive_01.jpg

xcarchive_02.jpg

這個(gè).xcarchive文件包含了應(yīng)用森缠、符號(hào)表信息以及其他的資源,可以右鍵 -> 顯示包內(nèi)容進(jìn)行查看仪缸,主要包含以下文件:


xcarchive文件類型
  • BCSymbolMaps:Xcode 對(duì) BitCode 符號(hào)表進(jìn)行混淆(Symbol Hiding)后生成的對(duì)照表贵涵,和 dSYM 文件會(huì)一一對(duì)應(yīng)

  • dSYMs:存儲(chǔ)此次編譯的符號(hào)表(debug symbols),用來符號(hào)化解析崩潰堆棧

  • info.plist: 項(xiàng)目target的配置文件

  • Products:存儲(chǔ)此次編譯生成的的 App 包(.app)恰画。雖然這個(gè)文件包含了App 運(yùn)行需要的可執(zhí)行文件以及其它資源宾茂,但是和最終用戶下載的版本會(huì)有所不同。后續(xù)的 export 操作會(huì)對(duì)其進(jìn)行進(jìn)一步處理拴还。

  • SCMBlueprint:如果 Xcode 打開了版本管理(Preferences -> Source Control -> Enable Source Control)跨晴,SCMBlueprint 文件夾會(huì)存儲(chǔ)此次編譯的版本控制信息,包括使用的 git 版本片林、倉(cāng)庫(kù)端盆、分支等怀骤。如果需要回溯此次編譯的源碼版本,可以在這個(gè)文件中找到對(duì)應(yīng)的信息焕妙。

  • SwiftSupport:在 Target 的 Build Settings 中打開了ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES蒋伦,此次編譯使用的 Swift 版本對(duì)應(yīng)的標(biāo)準(zhǔn)庫(kù)文件(.dylib)會(huì)被放到這個(gè)文件夾中。發(fā)布App時(shí)焚鹊,也會(huì)被復(fù)制到ipa bundle中凉敲。在iOS 12.2及以上,swift的ABI趨于穩(wěn)定寺旺,已經(jīng)不用再自帶鏈接庫(kù)了爷抓,因此ipa包節(jié)省了一定的體積

Export打包分發(fā)

Export主要是的對(duì)生成的.xcarchive 文件進(jìn)行進(jìn)一步的處理,生成不同渠道的ipa包阻塑,進(jìn)行分發(fā)蓝撇,Organize -> Archive文件 -> Distribute App

Export_01

這里對(duì)應(yīng)4種分發(fā)渠道:AppStore、Ad Hoc陈莽、Enterprise和Development渤昌,然后一步步往下操作即可。


Export_02.jpg

以上4中渠道對(duì)應(yīng)的打包method分別是app-store走搁、ad-hoc独柑、enterprise、development私植,可以在導(dǎo)出的文件夾中ExportOptions.plist文件看到對(duì)應(yīng)參數(shù)忌栅,如下所示

method參數(shù).jpg

最終Export導(dǎo)出的文件夾中主要包含以下4種文件


導(dǎo)出ipa的文件夾內(nèi)容.jpg
  • DistributionSummary.plist:包含ipa所支持的架構(gòu)、bitcode曲稼、證書索绪、embeddedBinaries(非系統(tǒng)的動(dòng)態(tài)庫(kù)相關(guān))、entitlements(apns環(huán)境贫悄、application-identifier等)瑞驱、profile等相關(guān)信息

  • ExportOptions.plist:ipa包導(dǎo)出的配置文件,主要包含包導(dǎo)出方式窄坦、簽名方式唤反、App Developer team的id等

  • MIStore.ipa:應(yīng)用的ipa包,包含了App所需的簽名鸭津、二進(jìn)制包彤侍、資源等

  • Packaging.log:打包相關(guān)的日志

如果想要查看ipa中的內(nèi)容,可以將.ipa改成.zip曙博,然后解壓拥刻,也可使用命令解壓
zip -0 -y -r myAppName.ipa Payload/,其中是.app的文件父泳,查看其包內(nèi)容般哼,主要包含以下幾部分

  • MachO可執(zhí)行文件:具體的介紹可查看iOS逆向 12:Mach-O文件(上)iOS逆向 12:Mach-O文件(下)
  • 簽名文件:App 的簽名信息會(huì)被放到 _CodeSignature 文件夾中吴汪。
  • info.plist:存儲(chǔ) App 主要信息的 plist 文件也會(huì)被一并打包到 ipa 中。
  • entitlements:App包含的相關(guān)權(quán)限蒸眠,在這個(gè)文件中通過 xml 的格式將這些授權(quán)記錄下來漾橙,例如Push notifications、App Group等
  • App Plugins:如果App實(shí)現(xiàn)了 App Extension擴(kuò)展楞卡,擴(kuò)展的包會(huì)以 .appex 的后綴存儲(chǔ)在 PlugIns 文件夾中霜运,隨著App的安裝一起安裝到用戶手機(jī)上
  • 鏈接庫(kù):App 運(yùn)行所需要的各種鏈接庫(kù)會(huì)被放入 Frameworks 文件夾。
  • 資源文件:App 運(yùn)行需要的各種資源文件也是 ipa 體積的大頭蒋腮。常見的有
    • 各種多媒體資源:圖片淘捡、音視頻
    • xib 文件:.nib.storyboardc
    • 各種打包的資源 .bundle
    • 其它類型的資源:字體、數(shù)據(jù)庫(kù)池摧、證書等等

除了Xcode自帶的工具application loader上傳ipa焦除,還可以通過Transporter上傳。Transporter可以以簡(jiǎn)單輕松的方式將內(nèi)容交付到Apple作彤,提供如下功能:

  • 只需要將ipa包拖放到Transporter中即可開始使用
  • 同時(shí)驗(yàn)證和上傳多個(gè)文件以快速交付
  • 查看交付進(jìn)度(警告膘魄、錯(cuò)誤和交付日志),以便快速修復(fù)交付問題
  • 查看歷史交付記錄(包含日期、時(shí)間)

所以,綜上所述咬扇,通過Xcode提供的打包方式比較繁瑣,需要人為操作許多步驟鬼佣,那么我們是否可以通過其他的方式進(jìn)行簡(jiǎn)化呢?答案是當(dāng)然可以呀。

簡(jiǎn)化打包操作的方式的主要有兩種

  • Shell打包腳本
  • Jenkins部署自動(dòng)打包

這里我們主要介紹Shell打包腳本的方式,主要分為以下幾步

  • 配置打包相關(guān)的名稱:.xcworkspace的名字逻杖、scheme名稱、編譯方式思瘟、打包方式、profile文件闻伶、bundleID等
  • 配置打包的相關(guān)路徑:導(dǎo)出路徑滨攻、archive文件路徑、ipa文件路徑蓝翰、ExportOptions.plist文件路徑
  • 清理工程:通過xcodebuild clean清理
  • 編譯:通過xcodebuild archive編譯光绕、歸檔工程
  • 導(dǎo)出ipa:通過xcodebuild命令將.xcarchive導(dǎo)出為ipa文件
  • 校驗(yàn)并上傳AppStore:如果是AppStore的包,通過xcrun altool校驗(yàn)并上傳AppStore

使用方式:進(jìn)入podfile所在目錄畜份,在終端執(zhí)行.sh文件诞帐,例如:sh xxxx.sh

具體的shell腳本如下(注:使用時(shí),需要自行填充其中的xxxxx所在的腳本代碼)

#!/bin/bash

# 前提:enterprise打包時(shí)爆雹,需要切換到 企業(yè)賬戶 及 BundleID

# 使用方法:
# step1: 將該腳本放在工程的根目錄下(跟.xcworkspace文件or .xcodeproj文件同目錄)
# step2: 根據(jù)情況修改下面的參數(shù)
# step3: 打開終端停蕉,執(zhí)行腳本愕鼓。(輸入sh,然后將腳本文件拉到終端慧起,會(huì)生成文件路徑菇晃,然后enter就可)

echo "-----------開始執(zhí)行腳本-----------"

# =============項(xiàng)目自定義部分(自定義好下列參數(shù)后再執(zhí)行該腳本)=================== #
echo "請(qǐng)選擇打包方式 ? [1:enterprise_debug 2:enterprise_release 3:ad_hoc 4:app_store]"

read number
while([[ $number != 1 ]] && [[ $number != 2 ]] && [[ $number != 3 ]] && [[ $number != 4 ]])
do
echo "Error! Should enter 1 or 2 or 3 or 4"
echo "請(qǐng)選擇打包方式 ? [ 1:enterprise_debug 2:enterprise_release 3:ad_hoc 4:app_store]"
read number
done

#-----------腳本配置信息-----------
# .xcworkspace的名字,必填
workspace_name="xxxxx"

# 指定項(xiàng)目的scheme名稱(也就是工程的target名稱)蚓挤,必填
scheme_name="xxxxx"

# 指定要打包編譯的方式 : Release,Debug磺送。一般用Release。必填
build_configuration="Release"

# method灿意,打包的方式估灿。方式分別為 development, ad-hoc 。必填
method="enterprise"

#  下面兩個(gè)參數(shù)只是在手動(dòng)指定Pofile文件的時(shí)候用到缤剧,如果使用Xcode自動(dòng)管理Profile,直接留空就好
# (跟method對(duì)應(yīng)的)mobileprovision文件名甲捏,需要先雙擊安裝.mobileprovision文件.手動(dòng)管理Profile時(shí)必填
# mobileprovision_name="9d8c7290-4345-4ebf-82d4-a74cab2ea40b"
mobileprovision_name=""

# 項(xiàng)目的bundleID,手動(dòng)管理Profile時(shí)必填
bundle_identifier=""
# if [[ $number == 1 ]]; then
#     bundle_identifier="com.mi.global.sho"
# else
#     bundle_identifier="com.mi.global.shop"
# fi

# 每次編譯后是否Build自動(dòng)加1,
# 可以修改該常量的值,以決定編譯后還是打包后Build自動(dòng)加1
# #  0: 每次打包后Build自動(dòng)加1  
# #  1: 每次編譯后Build自動(dòng)加1  
DEBUG_ENVIRONMENT_SYMBOL=0


# 根據(jù)選項(xiàng)配置不同的包
if [ $number == 1 ];then 
build_configuration="Debug"
method="enterprise"
DEBUG_ENVIRONMENT_SYMBOL=1
elif [[ $number == 2 ]]; then
build_configuration="Release"
method="enterprise"
DEBUG_ENVIRONMENT_SYMBOL=1
elif [[ $number == 3 ]]; then
build_configuration="Release"
method="ad-hoc"
DEBUG_ENVIRONMENT_SYMBOL=1
else 
build_configuration="Release"
method="app-store"
DEBUG_ENVIRONMENT_SYMBOL=1
fi

echo "--------------------腳本配置參數(shù)檢查--------------------"
echo "\033[33;1mworkspace_name = ${workspace_name}"
echo "scheme_name = ${scheme_name}"
echo "build_configuration = ${build_configuration}"
echo "bundle_identifier = ${bundle_identifier}"
echo "method = ${method}"
echo "mobileprovision_name = ${mobileprovision_name} \033[0m"

# =======================腳本的一些固定參數(shù)定義(無特殊情況不用修改)====================== #
# 獲取當(dāng)前腳本所在目錄
script_dir="$( cd "$( dirname "$0"  )" && pwd  )"
# 工程根目錄
project_dir=$script_dir

# 指定輸出導(dǎo)出文件夾路徑
export_path="$project_dir/Build"
# 指定輸出歸檔文件路徑
export_archive_path="$export_path/$scheme_name.xcarchive"
# 指定輸出ipa文件夾路徑
export_ipa_path="$export_path/"
# 指定導(dǎo)出ipa包需要用到的plist配置文件的路徑
export_options_plist_path="$project_dir/ExportOptions.plist"

echo "--------------------腳本固定參數(shù)檢查--------------------"
echo "\033[33;1mproject_dir = ${project_dir}"
echo "export_path = ${export_path}"
echo "export_archive_path = ${export_archive_path}"
echo "export_ipa_path = ${export_ipa_path}"
echo "export_options_plist_path = ${export_options_plist_path}\033[0m"

# =======================自動(dòng)打包部分(無特殊情況不用修改)====================== #
echo "------------------------------------------------------"
echo "\033[32m開始構(gòu)建項(xiàng)目  \033[0m"
# 進(jìn)入項(xiàng)目工程目錄
cd ${project_dir}

# 指定輸出文件目錄不存在則創(chuàng)建
if [ -d "$export_path" ];
then rm -rf "$export_path"
fi

/usr/bin/xcrun xcodebuild -UseNewBuildSystem=YES -xcconfig InnerXcconfig/innerInner/tt.xcconfig

# 編譯前清理工程
xcodebuild clean -workspace ${workspace_name}.xcworkspace \
                 -scheme ${scheme_name} \
                 -configuration ${build_configuration}

xcodebuild archive -workspace ${workspace_name}.xcworkspace \
                   -scheme ${scheme_name} \
                   -configuration ${build_configuration} \
                   -archivePath ${export_archive_path}

#  檢查是否構(gòu)建成功
#  xcarchive 實(shí)際是一個(gè)文件夾不是一個(gè)文件所以使用 -d 判斷
if [ -d "$export_archive_path" ] ; then
    echo "\033[32;1m項(xiàng)目構(gòu)建成功 ?? ?? ??  \033[0m"
else
    echo "\033[31;1m項(xiàng)目構(gòu)建失敗 ?? ?? ??  \033[0m"
    exit 1
fi
echo "------------------------------------------------------"

echo "\033[32m開始導(dǎo)出ipa文件 \033[0m"

# 先刪除export_options_plist文件
if [ -f "$export_options_plist_path" ] ; then
    #echo "${export_options_plist_path}文件存在鞭执,進(jìn)行刪除"
    rm -f $export_options_plist_path
fi

# 根據(jù)參數(shù)生成export_options_plist文件
/usr/libexec/PlistBuddy -c  "Add :method String ${method}"  $export_options_plist_path
/usr/libexec/PlistBuddy -c  "Add :provisioningProfiles:"  $export_options_plist_path
/usr/libexec/PlistBuddy -c  "Add :provisioningProfiles:${bundle_identifier} String ${mobileprovision_name}"  $export_options_plist_path
/usr/libexec/PlistBuddy -c  "Add :compileBitcode bool NO" $export_options_plist_path


xcodebuild  -exportArchive \
            -archivePath ${export_archive_path} \
            -exportPath ${export_ipa_path} \
            -exportOptionsPlist ${export_options_plist_path} \
            -allowProvisioningUpdates

# 檢查文件是否存在
if [ -f "$export_ipa_path/$scheme_name.ipa" ] ; then
    echo "\033[32;1m導(dǎo)出 ${scheme_name}.ipa 包成功 ??  ??  ??   \033[0m"
    open $export_path
else
    echo "\033[31;1m導(dǎo)出 ${scheme_name}.ipa 包失敗 ?? ?? ??     \033[0m"
    exit 1
fi

# 刪除export_options_plist文件(中間文件)
if [ -f "$export_options_plist_path" ] ; then
    #echo "${export_options_plist_path}文件存在司顿,準(zhǔn)備刪除"
    rm -f $export_options_plist_path
fi

# 輸出打包總用時(shí)
echo "\033[36;1m使用AutoPackageScript打包總用時(shí): ${SECONDS}s \033[0m"
echo "------------------------------------------------------"

# AppStore上傳到xxx
if [ $number == 4 ];then 
        # 將包上傳AppStore  
        ipa_path="$export_ipa_path/$scheme_name.ipa"
        # 上傳AppStore的密鑰ID、Issuer ID
        api_key="xxxxx"
        issuer_id="xxxxx"

    echo "--------------------AppStore上傳固定參數(shù)檢查--------------------"
    echo "ipa_path = ${ipa_path}"
    echo "api_key = ${api_key}"
    echo "issuer_id = ${issuer_id}"

# 校驗(yàn) + 上傳 方式1
    # # 校驗(yàn)指令
    # cnt0=`xcrun altool --validate-app -f ${ipa_path} -t ios --apiKey ${api_key} --apiIssuer ${issuer_id} --verbose`
    # echo $cnt0
    # cnt=`echo $cnt0 | grep “No errors validating archive” | wc -l`

    # if [ $cnt = 1 ] ; then
    #     echo "\033[32;1m校驗(yàn)IPA成功??  ??  ?? \033[0m"
    #     echo "------------------------------------------------------"
    #     cnt0=`xcrun altool --upload-app -f ${ipa_path} -t ios --apiKey ${api_key} --apiIssuer ${issuer_id} --verbose"`
    #     echo $cnt0
    #     cnt=`echo $cnt0 | grep “No errors uploading” | wc -l`
    #     if [ $cnt = 1 ] ; then
    #         echo "\033[32;1m上傳IPA成功??  ??  ?? \033[0m"
    #         echo "------------------------------------------------------"
            
    #     else
    #         echo "\033[32;1m上傳IPA失敗?? ?? ??   \033[0m"
    #         echo "------------------------------------------------------"
    #     fi
    # else
    #     echo "\033[32;1m校驗(yàn)IPA失敗?? ?? ??   \033[0m"
    #     echo "------------------------------------------------------"
    # fi

# 校驗(yàn) + 上傳 方式2
    # 驗(yàn)證
    validate="xcrun altool --validate-app -f ${ipa_path} -t ios --apiKey ${api_key} --apiIssuer ${issuer_id} --verbose"
    echo "running validate cmd" $validate
    validateApp="$($validate)"
    if [ -z "$validateApp" ]; then
        echo "\033[32m校驗(yàn)IPA失敗?? ?? ??   \033[0m"
        echo "------------------------------------------------------"
    else
        echo "\033[32m校驗(yàn)IPA成功??  ??  ??  \033[0m"
        echo "------------------------------------------------------"
        
        # 上傳
        upload="xcrun altool --upload-app -f ${ipa_path} -t ios --apiKey ${api_key} --apiIssuer ${issuer_id} --verbose"
        echo "running upload cmd" $upload
        uploadApp="$($upload)"
        echo uploadApp
        if [ -z "$uploadApp" ]; then
            echo "\033[32m傳IPA失敗?? ?? ??   \033[0m"
            echo "------------------------------------------------------"
        else
            echo "\033[32m上傳IPA成功??  ??  ?? \033[0m"
            echo "------------------------------------------------------"
        fi

    fi

fi

exit 0

這里額外補(bǔ)充下xcodebuild兄纺、xcrun命令的知識(shí):

xcodebuild
用于編譯xcode中的projects和workspaces,可通過man xcodebuild查看文檔

iOS_lowlevel_38_01_12.jpg

常見的命令格式如下:

  • 清理工程:
xcodebuild clean -workspace [xcworkspace路徑] \
                -scheme [scheme名稱] \
                -configuration [編譯方式Release或Debug]
  • 編譯工程
xcodebuild archive -workspace [xcworkspace路徑] \
                   -scheme [scheme名稱]\
                   -configuration [編譯方式Release或Debug] \
                   -archivePath [.xcarchive文件路徑]
  • 導(dǎo)出ipa
xcodebuild  -exportArchive \
            -archivePath [.xcarchive文件路徑] \
            -exportPath [.ipa文件路徑] \
            -exportOptionsPlist [ExportOptions.plist文件路徑] \
            -allowProvisioningUpdates

xcrun
用于運(yùn)行或定位開發(fā)工具以及屬性大溜,也可以通過man xcrun查看具體文檔

xcrun文檔.jpg

常見的命令格式如下:

  • 校驗(yàn)ipa
xcrun altool --validate-app -f [.ipa的路徑] -t ios --apiKey [api_key] --apiIssuer [issuer id] --verbose
  • 上傳ipa到AppStore
xcrun altool --upload-app -f [.ipa的路徑] -t ios --apiKey [api_key] --apiIssuer [issuer id] --verbose

其中上傳所需的api key 和 issuer id需要在AppStore Connect中配置和獲取。使用具有賬戶管理權(quán)限的賬號(hào)登陸App Store Connect 選擇 用戶與訪問 >密鑰 查詢信息:issuser和apiKey(密鑰 ID)估脆,如下所示钦奋,

密鑰.jpg

并將公鑰下載到本地,并將下載好的p8文件保存到需要放到一個(gè)固定目錄下

P8文件放入以下文件中

./private_keys
~/private_keys
~/.private_keys
~/.appstoreconnect/private_keys

除了使用xcrun altool上傳ipa疙赠,我們還可以使用Transpoter付材,但相對(duì)來說,Shell腳本更方便圃阳,實(shí)現(xiàn)了本地自動(dòng)化打包上傳厌衔。

綜上所述,shell腳本打包整體流程如下


shell腳本打包流程圖示
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末捍岳,一起剝皮案震驚了整個(gè)濱河市富寿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锣夹,老刑警劉巖页徐,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異银萍,居然都是意外死亡变勇,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門贴唇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搀绣,“玉大人飞袋,你說我怎么就攤上這事⊥阆ǎ” “怎么了授嘀?”我有些...
    開封第一講書人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)锣险。 經(jīng)常有香客問我蹄皱,道長(zhǎng),這世上最難降的妖魔是什么芯肤? 我笑而不...
    開封第一講書人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任巷折,我火速辦了婚禮,結(jié)果婚禮上崖咨,老公的妹妹穿的比我還像新娘锻拘。我一直安慰自己,他們只是感情好击蹲,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開白布署拟。 她就那樣靜靜地躺著,像睡著了一般歌豺。 火紅的嫁衣襯著肌膚如雪推穷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,328評(píng)論 1 310
  • 那天类咧,我揣著相機(jī)與錄音馒铃,去河邊找鬼。 笑死痕惋,一個(gè)胖子當(dāng)著我的面吹牛区宇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播值戳,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼议谷,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了述寡?” 一聲冷哼從身側(cè)響起柿隙,我...
    開封第一講書人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鲫凶,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體衩辟,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡螟炫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了艺晴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昼钻。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡掸屡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出然评,到底是詐尸還是另有隱情仅财,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布碗淌,位于F島的核電站盏求,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏亿眠。R本人自食惡果不足惜碎罚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纳像。 院中可真熱鬧荆烈,春花似錦、人聲如沸竟趾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽岔帽。三九已至玫鸟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間山卦,已是汗流浹背鞋邑。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留账蓉,地道東北人枚碗。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像铸本,于是被迫代替她去往敵國(guó)和親肮雨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

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