Swift進階-工程化實踐(一)

swift制作framework靜態(tài)庫
swift工程化實踐(一)
swift工程化實踐(二)

一、認識.swiftmodule目錄文件

當OC使用Swift的時候秘症,是通過ProjectName-Swift.h暴露給OC无午;
swift在使用OC的時候蕉堰,是通過ProjectName.Bridging-Header.h姓言。
如下圖:

混編

但是在創(chuàng)建Swift Framework(swift組件)的時候莽龟,就不能使用這個ProjectName.Bridging-Header.h橋接文件了!

Swift與OC之間混編中出現(xiàn)的三個問題:

  • Swift 沒有頭文件地梨,只有 .swiftmodule 目錄
  • Swift Framework 不能使用 ProjectName.Bridging-Header.h
  • Swift設(shè)計到編譯參數(shù)
.swiftmodule概念:

在Xcode9之后菊卷,Swift開始支持靜態(tài)庫。Swift沒有頭文件概念宝剖,那外界要使用Swift中用public修飾的類和函數(shù)怎么辦呢洁闰?
Swift庫引入了一個全新的文件 .swiftmodule

.swiftmodule目錄文件在哪里万细?
在我們寫好的Framework編譯后扑眉,類似我們工程中產(chǎn)生的可執(zhí)行文件目錄里

.swiftmodule包含了三種文件:

  • .swiftmodule: 包含序列化過后的AST(抽象語法樹 Abstract Syntax Tree)以及 SIL(Swift中間語言 Swift Itermediate Language)
  • .swiftdoc :用戶文檔
  • .swiftinterface: Module stability

疑問:為什么工程里A.swift訪問到了.swiftmodule 就能訪問到 B.swift?
我們知道訪問限制的幾個關(guān)鍵字 open雅镊、public襟雷、internal刃滓、fileprivate仁烹、private。A.swift想要訪問到B.swift的類或函數(shù)判斷訪問限制的邏輯就在.swiftmodule里邊咧虎。

番外:swift制作framework靜態(tài)庫

二卓缰、了解使用xcconfig

Xcode就是一個大型的shell環(huán)境,在這個環(huán)境中可以調(diào)各種工具 clang/swiftc等砰诵,而這個工具里邊需要使用到很多的參數(shù)征唬;這些參數(shù)有兩種方式配置和管理:

  • 使用Xcode內(nèi)置的 Build Settings(沒有暴露的變量)
  • 使用xcconfig
1、工程中配置xcconfig

新建一個Framework工程

新建一個Framework工程

新建一個xcconfig文件茁彭,取名就叫Config


新建一個xcconfig文件1
新建一個xcconfig文件2

xcconfig幫助文檔: https://help.apple.com/xcode/#/dev745c5c974
Xcode Build Settings 對應(yīng)的 xcconfig 變量

  • 讓這個xcconfig生效:

找到Project-> Info -> Configurations总寒,給想要使用這個文件的Project或者是Targets去設(shè)置(需區(qū)分DebugRelease環(huán)境的)

  • 配置xcconfig里的shell參數(shù):

比如說給我們的鏈接器做配置,在Build Settings里找到Other Linker Flags就能找到對應(yīng)xcconfig對應(yīng)參數(shù)的key:OTHER_LDFLAGS

于是乎就可以在xcconfig設(shè)置參數(shù)的value:

xcconfig設(shè)值

當我們把xcconfig的值設(shè)置好之后理肺,Build Settings里的Other Linker Flags就會發(fā)生變化了:

Build Settings發(fā)生變化
  • 配置shell參數(shù)的優(yōu)先級問題:
    比如我們Build Settings里的Other Linker Flags已經(jīng)有一個值是 -framework "CoreImage"摄闸,那我們還往xcconfig配置OTHER_LDFLAGS的值是 -framework "Foundation",這個時候就沖突啦妹萨,會發(fā)現(xiàn)Build Settings的值并沒有發(fā)生改變年枕,依舊是原來的值,那我們這個配置肯定是有一個優(yōu)先級的乎完。

優(yōu)先級由高到低:
1.手動配置 TARGETS 的 Build Settings熏兄;
2.TARGETS 中配置了 xcconfig 文件;
3.手動配置 PROJECT 的 Build Settings;
4.PROJECT 中配置了 xcconfig 文件摩桶。

$(inherited)的作用是配置繼承
如果我們想要-framework "CoreImage"-framework "Foundation"桥状,就可以在Build Settings里的 Other Linker Flags最前面添加$(inherited):、硝清、岛宦、

$(inherited)的作用
  • 導(dǎo)入其它xcconfig配置:

1.在創(chuàng)建 xcconfig 文件可以根據(jù)需求創(chuàng)建多個。也就意味著耍缴,可以通過 include 關(guān)鍵字導(dǎo)入:

#include "Debug.xcconfig"

2.通過絕對路徑導(dǎo)入:

#include "/Users/xxx/Desktop/MyFramework/MyFramework/Debug.xcconfig"

3.通過相對路徑砾肺,已${SRCROOT}路徑為開始導(dǎo)入:

#include "MyFramework/Debug.xcconfig"
2、xcconfig中配置參數(shù)變量

變量定義按照OC命名規(guī)則防嗡,僅由大寫字母变汪、數(shù)字和下劃線組成,原則上是大寫實際上也可以不大寫蚁趁。字符串可以是"號也可以是'號裙盾。

變量有三種特殊情況:
1.xcconfigBuild Settings定義的變量是一致的,那么會發(fā)生覆蓋現(xiàn)象(上文已說明)他嫡,可以通過$(inherited)番官,讓當前變量繼承該變量的原有值:
Config.xcconfig

#include "Debug.xcconfig"
OTHER_LDFLAGS = $(inherited) -framework "CoreData"

Debug.xcconfig

OTHER_LDFLAGS = $(inherited) -framework "CoreText"

來看Build Settings并沒有顯示有-framework "CoreText"

Build Settings

其實我們已經(jīng)導(dǎo)入成功了洽瞬,來看看通過輸出環(huán)境變量贞间,再重新編譯一下忽肛,看看這個變量是否有導(dǎo)入這三個:

設(shè)置命令
導(dǎo)入成功證明

2.引入變量脖祈,使用$()或者${}都可行

引入變量

3.條件變量呻右,根據(jù)SDK唯咬、ArchConfiguration對設(shè)置進行條件化:

// 指定該 Configuration 是 Debug模式下生效
// 指定該 SDK 是 模擬器 還有 iphoneos* 或 macos* 等
// 指定生效框架位 x86_64
OTHER_LDFLAGS[config=Debug][sdk=iphonesimulator*][arch=x86_64] = $(inherited) -framework "AFNetworking"

注意:在Xcode11.4版本之后贮缕,可以使用 default 來指定變量空時的默認值:

$(BUILD_SETTING_NAME:default=value)

三惜浅、輸出.swiftmodule內(nèi)容

通過Swift REPL來輸出.swiftmodule目錄下的文件到底是什么染乌。REPL有一種方式可以輸出.swiftmodule到底是什么東西山孔。
Swift REPL是Swift解析器,用來調(diào)試swift代碼)

1.啟動REPL環(huán)境
打開終端輸入

$ swift -frontend -repl

會報如下錯誤 error: unable to load standard library for target 'x86_64-apple-macosx12.0'
是因為在終端里使用的編譯工具(命令里的 swift) 其實是Xcode內(nèi)置的荷憋,我當前是Xcode12版本台颠,就需要這個x86_64-apple-macosx12.0 這個SDK。所以我們找一下這個SDK在哪:

$ xcrun -show-sdk-path

會輸出SDK的路徑:/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk

于是得到這個命令勒庄,滿心歡喜地嘗試

/**
 -frontend:使用Swift前端工具
 -repl:進入解釋器
 -sdk:環(huán)境使用的SDK
 -F:framework所在的路徑
 -I:library所在的路徑
 :print_module <name> :打印module聲
*/
$ swift -frontend -repl -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk
進入repl解析器失敗

這是因為這個命令里 swift 是Xcode內(nèi)置的命令行工具串前,一般出現(xiàn)的這樣的問題:首先要知道Xcode內(nèi)置編譯器都是由 LLVM官方拉取分支,在這基礎(chǔ)上做了一些添加锅铅、修改和屏蔽酪呻,所以導(dǎo)致上面報錯我也辦法通過Xcode去使用REPL,并且提示你去使用LLDB的方式盐须。 當你去使用該方式就會顯得復(fù)雜了許多許多不好整玩荠。

解決方式:
自己通過LLVM編譯出自己的swift編譯器。
打開編譯后的swift源碼找到目錄: swift-source -> build -> Ninja-RelWithDebInfoAssert+stdlib-DebugAssert -> swift-macosx-x86_64 -> bin -> swift和swiftc

swift源碼目錄

把這個swift編譯器拖拽到命令行,繼續(xù) 啟動REPL環(huán)境(文件路徑自行更改)

$ /Users/xxx/Desktop/swift-source/build/Ninja-RelWithDebInfoAssert+stdlib-DebugAssert/swift-macosx-x86_64/bin/swift -frontend -repl -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk
進入了REPL解析器

我們是進入了REPL解析器阶冈,但我們沒辦法進入下一步闷尿,需要給定這個命令需要輸出的.swiftmodule目錄:

Products目錄
// 先退出當前解析器: `$ quit`
$ quit 
/**
 -frontend:使用Swift前端工具
 -repl:進入解釋器
 -sdk:環(huán)境使用的SDK
 -F:framework所在的路徑
 -I:library所在的路徑
 :print_module <name> :打印module聲
*/
$ /Users/xxx/Desktop/swift-source/build/Ninja-RelWithDebInfoAssert+stdlib-DebugAssert/swift-macosx-x86_64/bin/swift -frontend -repl -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -F /Users/xxx/Desktop/Framework/Products
最終啟動并進入了REPL解析器

2.輸出.swiftmodule所代表的信息
進入了REPL解析器后,輸出Framework工程生成的Framework.swiftmodule所代表的信息:

:print_module <name> // 這個name是module的名稱女坑,這里的我是Framework

于是乎又報了好多錯填具,這是因為我剛才通過LLVM編譯出來的swift編譯器與當前的SDK版本功能對應(yīng)不上。
那我可以把Framework工程匹配的SDK更換掉就可以了:往Config.xcconfig添加參數(shù)匆骗,然后再重新編譯生成新的Framework.framework

// Xcode內(nèi)置的swift編譯器劳景,這里使用swiftc是因為比swift多了一些參數(shù)
SWIFT_EXEC = /Users/xxx/Desktop/swift-source/build/Ninja-RelWithDebInfoAssert+stdlib-DebugAssert/swift-macosx-x86_64/bin/swiftc
// 更換SDK版本
SDKROOT = /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk

再打開命令行啟動REPL環(huán)境

$ /Users/xxx/Desktop/swift-source/build/Ninja-RelWithDebInfoAssert+stdlib-DebugAssert/swift-macosx-x86_64/bin/swift -frontend -repl -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -F /Users/xxx/Desktop/Framework/Products

輸出.swiftmodule所代表的信息

:print_module Framework

最終輸出Framework.swiftmodule內(nèi)容如下:

@_exported import Foundation // 根據(jù) Framework.h 導(dǎo)入的 #import <Foundation/Foundation.h>
var SWIFT_TYPEDEFS: Int32 {
    get {
        return
    }
}
typealias char16_t = uint_least16_t
typealias char32_t = uint_least32_t
typealias swift_double2 = SIMD2<Double>
typealias swift_double3 = SIMD3<Double>
typealias swift_double4 = SIMD4<Double>
typealias swift_float2 = SIMD2<Float>
typealias swift_float3 = SIMD3<Float>
typealias swift_float4 = SIMD4<Float>
typealias swift_int2 = SIMD2<Int32>
typealias swift_int3 = SIMD3<Int32>
typealias swift_int4 = SIMD4<Int32>
typealias swift_uint2 = SIMD2<UInt32>
typealias swift_uint3 = SIMD3<UInt32>
typealias swift_uint4 = SIMD4<UInt32>
import Foundation // 這是在Teacher.swift里導(dǎo)入的Foundation,所以別的.swift文件也能訪問到
@_exported import Framework
import SwiftOnoneSupport
struct Teacher {
    init()
    var An: Int
    var Lin: String
}

這是我的Teacher.swift的聲明:

import Foundation
public struct Teacher {
    public init() {}
    public var An = 1
    public var Lin = "工程化"
}

這就解釋我們在一個swift文件中導(dǎo)入了頭文件碉就,在其他swift文件中就能訪問到了盟广,所導(dǎo)入的都會在swiftmodule!

.swiftmodule保存了編譯器對swift代碼分析之后的記錄。

四瓮钥、.swiftinterface

模擬模塊不穩(wěn)定:
1.新建一個工程取名App當前Xcode的swift編譯器版本是5.5.1
2.再構(gòu)建一個Framework筋量,但是這個Framework是是在swift編譯器版本5.2.4下生成的
3.把Framework導(dǎo)入到工程A,并使用Framework里的API碉熄,編譯能成功桨武!
4.如果把Framework里面的module里包含的.swiftinterface移除,再編譯的話就會報如下錯誤:

error: Module compiled with 5.2.4 cannot be imported by the Swift 5.5.1 compiler /Users/xxx/Desktop/App/Framework.framework/Modules/Framework.swiftmodule/x86_64-apple-macos.swiftmodule

.swiftinterfaceModule stability模塊的穩(wěn)定性锈津,是swift5.1推出解決模塊之間編譯器版本兼容問題呀酸。這就意味著不同版本編譯器構(gòu)建的swift模塊可以在同一個應(yīng)用程序中一起使用。

實際上.swiftinterface.swiftmodule是差不多的一姿,.swiftinterface多了一個解決兼容性的東西七咧。
編譯速度上.swiftinterface會更慢一些在編譯期間沒有模塊兼容性問題的時候叮叹,優(yōu)先使用.swiftmodule

五爆存、Library Evolution

Library Evolution:從swift5開始蛉顽,庫能夠聲明穩(wěn)定的 ABI(二進制通用接口),允許庫二進制文件替換為更新版本先较,而無需重新編譯客戶端程序携冤。

接下來看看一個案例:
1.創(chuàng)建一個工程取名App

2.創(chuàng)建一個動態(tài)庫Framework工程,保存在和App同一個目錄下

3.把兩個工程添加到一個xcworkspace里連調(diào)
打開App工程闲勺,在右上角選擇 file -> Save As Workspace曾棕,取名叫Muti,然后關(guān)閉App工程菜循,再打開Muti.xcworkspace

將動態(tài)庫Framework工程添加到xcworkspace

App工程關(guān)聯(lián)編譯

當編譯App的時候翘地,也會把Framework一起編譯。
在App的main.swift里調(diào)用Framework里的Teacher的API

import Framework
print(Teacher().Lin)

此時運行打印的結(jié)果是 2 沒有問題,因為編譯App就會連同F(xiàn)ramework一起編譯衙耕。

此時如果把Framework的Library Evolution關(guān)閉掉昧穿,把Teacher的An屬性注釋掉,重新單獨編譯Framework

public struct Teacher {
    public init() {}
//    public var An = "11111112312312"
    public var Lin = 2
}

編譯完Framework后橙喘,把Teacher里的Teacher的An屬性打開时鸵,然后選擇App進行 Run Without Building

Run Without Building的作用是不重新編譯App。此時main.swift打印的結(jié)果是一串數(shù)字不是2厅瞎。

因為Swift是靜態(tài)語言饰潜,它的底層數(shù)據(jù)結(jié)構(gòu)在編譯的時候就已經(jīng)確定了,而Framework的Teacher結(jié)構(gòu)更新了和簸,并沒有重新編譯到App囊拜,導(dǎo)致在訪問Lin的時候是通過偏移量和字節(jié)對齊去往內(nèi)存找,結(jié)果找到的值不是原來的東西了比搭。

蘋果在swift5.0的時候推出了Library Evolution冠跷,把部分代碼從編譯器確定了推到運行期,引入了swift運行時身诺。
如果我們把Framework的Library Evolution打開蜜托,就會打印出正常的2了

那開啟了Library Evolution又會引發(fā)另外一個問題:本身swift就是靜態(tài)語音它的速度很快,如果把代碼推到運行時的話霉赡,會導(dǎo)致性能的下降橄务,于是為了解決這個問題,可以使用關(guān)鍵字@frozen:

@frozen
public struct Teacher {
    public init() {}
    public var An = "11111112312312"
    public var Lin = 2
}

@frozen 的作用:被@frozen標記的代碼塊凍住穴亏,保持靜態(tài)性而不推到運行時蜂挪。

六、.modulemap

module是什么嗓化?
module是用來管理一組頭文件的

1.模塊探究

新建一個OC的項目取名為MyApp棠涮,創(chuàng)建一個MyModule模塊目錄,然后創(chuàng)建Teacher

ViewController里想使用Teacher有兩種方式導(dǎo)入

那如果我想要用Module去管理MyModule模塊目錄的頭文件呢刺覆?
首先在MyModule目錄下創(chuàng)建一個module.modulemap的文件

創(chuàng)建一個module.modulemap 的文件

聲明一個名為Teacher的module严肪;包含有Teacher.h頭文件(header "Teacher.h");又因為Teacher.h導(dǎo)入了Foundation庫谦屑,所以使用 export * 或者 export Foundation

module.modulemap

要把這個module生效驳糯,就要告訴給編譯器,所以新建一個MyApp.Debug.config.xcconfig(項目.環(huán)境.作用.xcconfig),由于當前編譯器是clang氢橙,這個這個xcconfig這樣寫酝枢。(ps:也可以直接在Build Settings上設(shè)置)

MyApp.Debug.config.xcconfig

當然依舊要使得這個xcconfig生效,還需要配置

配置xcconfig

最后悍手,當然是使用這個Teacher module

綜上使用了名為Teachermodule來管理MyModule模塊下的 Teacher.h頭文件帘睦。

還有一種導(dǎo)入module的方式是 #import <Teacher/Teacher.h>, 但是會發(fā)現(xiàn)它會報錯: 'Teacher/Teacher.h' file not found
因為這種書寫方式是 framework 專屬的書寫方式袍患。

但是這個module寫法有些弊端:如果我的MyModule模塊里有一百個的.h頭文件,那我總不可能一個一個寫 header "xxx.h"吧官脓。
新建一個Teacher-umbrella.h把要導(dǎo)入的頭文件放到這里

這樣就可以做到通過一個頭文件去管理一組頭文件

2.framework module

繼續(xù)上面的例子协怒,把module聲明成framework module 就會報錯: Umbrella header 'Teacher-umbrella.h' not found

framework module Teacher {
    // umbrella -> 一組
    umbrella header "Teacher-umbrella.h"
    // Teacher.h -> Foundation
    export *
}

那是因為 framework 是特殊的module,它包含了Header+.a+簽名+資源+Module卑笨,它更像是一個文件夾

前面看過.framework包含的東西了孕暇,所有的.h文件都放在Headers目錄下

于是我嘗試新建一個Headers目錄,把頭文件也放到這下面赤兴,這樣就編譯通過了妖滔!

既然聲明framework module成功了,但是在ViewController里使用 #import <Teacher/Teacher.h>依舊是不可以的桶良。
因為編譯器clang在識別Headersmodule.modulemap 必須在 framework 目錄(.framework結(jié)尾)下座舍。
于是乎我把MyModule目錄改成MyModule.framework

再把MyApp.Debug.config.xcconfig的路徑映射改一下,此時我的ViewController就能夠?qū)脒@個頭文件了(#import <MyModule/Teacher.h>#import <MyModule/Teacher-umbrella.h> 都可以了)

如果說我想用這樣的方式導(dǎo)入Teacher類:@import Teacher.Teacher;

module.modulemap可以這樣設(shè)置(ViewController里四種導(dǎo)入方式都可以用了:@import Teacher; @import Teacher.Teacher; #import <MyModule/Teacher.h> #import <MyModule/Teacher-umbrella.h>

explicit關(guān)鍵字在注釋上也有說明陨帆。


番外番外:
像我們的Swift生成的.framework里面的.modulemap文件(以第五部分的Framework.framework為例子)

番外番外
framework module Framework {
  umbrella header "Framework.h"

  export *
  module * { export * }
}

// 子Framework.Self -> Framework-Swift.h
// requires objc: 使用Framework.Swift的源碼文件是一個OC文件的時候
module Framework.Swift {
    header "Framework-Swift.h"
    requires objc
}

來看看 Framework-Swift.h 的源碼是OC


Framework-Swift.h

使用.modulemap的好處:.modulemap所管理的頭文件預(yù)編譯成pcm 預(yù)編譯的二進制文件曲秉,在編譯.m的時候就不用重復(fù)地去編譯.h,大大提升編譯效率和查找時間疲牵。

關(guān)于.modulemap相關(guān)demo可以自行下載

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末承二,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子纲爸,更是在濱河造成了極大的恐慌亥鸠,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件识啦,死亡現(xiàn)場離奇詭異负蚊,居然都是意外死亡,警方通過查閱死者的電腦和手機颓哮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門家妆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人题翻,你說我怎么就攤上這事揩徊。” “怎么了嵌赠?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長熄赡。 經(jīng)常有香客問我姜挺,道長,這世上最難降的妖魔是什么彼硫? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任炊豪,我火速辦了婚禮凌箕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘词渤。我一直安慰自己牵舱,他們只是感情好,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布缺虐。 她就那樣靜靜地躺著芜壁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪高氮。 梳的紋絲不亂的頭發(fā)上慧妄,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機與錄音剪芍,去河邊找鬼塞淹。 笑死,一個胖子當著我的面吹牛罪裹,可吹牛的內(nèi)容都是我干的饱普。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼状共,長吁一口氣:“原來是場噩夢啊……” “哼套耕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起口芍,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤箍铲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鬓椭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體颠猴,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年小染,在試婚紗的時候發(fā)現(xiàn)自己被綠了翘瓮。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡裤翩,死狀恐怖资盅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情踊赠,我是刑警寧澤呵扛,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站筐带,受9級特大地震影響今穿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜伦籍,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一蓝晒、第九天 我趴在偏房一處隱蔽的房頂上張望腮出。 院中可真熱鬧,春花似錦芝薇、人聲如沸胚嘲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽馋劈。三九已至,卻和暖如春灭红,著一層夾襖步出監(jiān)牢的瞬間侣滩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工变擒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留君珠,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓娇斑,卻偏偏與公主長得像策添,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子毫缆,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

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