SnapKit源碼解析

簡介

什么是Snapkit

  • SnapKit是一個(gè)使用 Swift 編寫而來的AutoLayout框架,通過使用Snapkit,我們可以通過簡短的代碼完成布局顿痪,如下所示:

原生布局

contentView.addSubview(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false

addConstraint(NSLayoutConstraint(item: imageView,
                                 attribute: .leading,
                                 relatedBy: .equal,
                                 toItem: contentView,
                                 attribute: .leading,
                                 multiplier: 1,
                                 constant: 0))

addConstraint(NSLayoutConstraint(item: imageView,
                                 attribute: .top,
                                 relatedBy: .equal,
                                 toItem: contentView,
                                 attribute: .top,
                                 multiplier: 1,
                                 constant: 0))

addConstraint(NSLayoutConstraint(item: imageView,
                                 attribute: .trailing,
                                 relatedBy: .equal,
                                 toItem: contentView,
                                 attribute: .trailing,
                                 multiplier: 1,
                                 constant: 0))

addConstraint(NSLayoutConstraint(item: imageView,
                                 attribute: .bottom,
                                 relatedBy: .equal,
                                 toItem: contentView,
                                 attribute: .bottom,
                                 multiplier: 1,
                                 constant: 0))

SnapKit布局:

contentView.addSubview(imageView)

imageView.snp.makeConstraints { make in
    make.edges.equalTo(contentView)
}
  • DSL(Domain specific Language)特定領(lǐng)域語言
    DSL是為了解決某些特定場景下的任務(wù)而專門設(shè)計(jì)的語言。如果能把一些設(shè)計(jì)師產(chǎn)出的長寬油够、色值蚁袭、文字、居中石咬、距上等設(shè)計(jì)元數(shù)據(jù)(設(shè)計(jì)的標(biāo)注信息等)揩悄,以一種約定的簡潔的語言規(guī)則(即DSL)輸入給程序代碼,由程序和代碼自動(dòng)的分析和處理鬼悠,從而生成真正的界面開發(fā)代碼setFrame删性,setTitle,setColor厦章,addSubview镇匀,這樣就可以大幅度的減少代碼量與工作量,程序員來寫這種簡潔的語法規(guī)則會(huì)更快更高效袜啃,甚至可以把這種簡潔的語法規(guī)則教會(huì)設(shè)計(jì)師汗侵,讓設(shè)計(jì)師有能力直接寫出DSL,然后輸入給底層程序,這樣界面就自然完成晰韵。

注意事項(xiàng)

  • 使用SnapKit前发乔,一定要先將子控件添加到父視圖中,否則會(huì)直接崩潰雪猪!
parentView.addSubview(subview)
  • leading和left栏尚、trailing和right

其實(shí)在目前國內(nèi)App中使用leading與left,trailing與right在正常情況下是等價(jià)的只恨,這是因?yàn)閲鴥?nèi)的閱讀習(xí)慣是從左到右的译仗,不過如果你的App需要在阿拉伯國家上架,他們的布局是從右至左時(shí)(比如阿拉伯文) 則會(huì)對(duì)調(diào)官觅。

建議使用leading和trailing纵菌,便于App國際化。

使用教程

  • 較為簡單休涤,api也不多咱圆,不多描述了

源碼解析

詳細(xì)解析

  • snp

lable.snp通過給view加擴(kuò)展實(shí)現(xiàn)的

public extension ConstraintView {
    public var snp: ConstraintViewDSL {
        return ConstraintViewDSL(view: self
    }
}

snp 最后是生成了一個(gè) ConstraintViewDSL 對(duì)象

  • ConstraintView的定義
if os(iOS) || os(tvOS)
    public typealias ConstraintView = UIView
#else public
    typealias ConstraintView = NSView
#endif

這里tvOS是基于 iOS的操作系統(tǒng),tvOS 是專門為第四代 Apple TV設(shè)計(jì)的操作系統(tǒng)功氨。

  • ConstraintViewDSL
internal init(view: ConstraintView) {
     self.view = view 
}

ConstraintViewDSL 類的構(gòu)造函數(shù)序苏,就是將 view 保存起來

public func makeConstraints(_ closure:
                            (_ make: ConstraintMaker) -> Void){
    ConstraintMaker.makeConstraints(item:self.view, closure: closure)
}

makeConstraints 函數(shù)將傳進(jìn)來的閉包傳遞給ConstraintMaker 這個(gè)類去處理了

internal static func makeConstraints(item: LayoutConstraintItem,closure: (_ make: ConstraintMaker) -> Void) {
     let constraints = prepareConstraints(item: item, closure: closure)
     for constraint in constraints {
         constraint.activateIfNeeded(updatingExisting: false)
    }
}

該方法主要調(diào)用了被接受prepareConstraints函數(shù)。

internal static func prepareConstraints(item: LayoutConstraintItem, closure: (_ make: ConstraintMaker) -> Void) -> [Constraint] {
    let maker = ConstraintMaker(item: item)
    closure(maker)
    var constraints: [Constraint] = []
    for description in maker.descriptions {
        guard let constraint = description.constraint else {
            continue
        }
        constraints.append(constraint)
    }
    return constraints
}

首先這里構(gòu)造一個(gè) maker捷凄,然后調(diào)用閉包忱详,閉包內(nèi)部會(huì)添加一些約束,接下來就是獲取這些約束, 最后將約束激活纵势。

閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)踱阿。例如在程序中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量钦铁,所以閉包可以理解成“定義在一個(gè)函數(shù)內(nèi)部的函數(shù)“在本質(zhì)上软舌,閉包是將函數(shù)內(nèi)部和函數(shù)外部連接起來的橋梁。

internal init(item: LayoutConstraintItem) {
    self.item = item
    self.item.prepare()
}

這是ConstraintMaker的構(gòu)造函數(shù)牛曹,這里出現(xiàn)了一個(gè)新的類型LayoutConstraintItem佛点,表示一個(gè)可布局的對(duì)象。

public protocol LayoutConstraintItem: class {
}

可以看到這是一個(gè)協(xié)議

extension ConstraintLayoutGuide : LayoutConstraintItem {
}
extension ConstraintView : LayoutConstraintItem {
}

ConstraintView 和 ConstraintLayoutGuide 都實(shí)現(xiàn)LayoutConstraintItem這個(gè)協(xié)議黎比。

extension LayoutConstraintItem {
    internal func prepare() {
        if let view = self as? ConstraintView {
            view.translatesAutoresizingMaskIntoConstraints = false
        }
    }
}

該協(xié)議實(shí)現(xiàn)了一些方法,包含prepare方法超营。這一步其實(shí)就是禁用 View 的 AutoresizeMask。

回到開始的閉包阅虫,里面我們寫的make.center.equalTo(self.view.snp.center)可以通過這個(gè)函數(shù)生成一些約束對(duì)象演闭。首先我們都知道, 每一個(gè)約束, 首先需要添加到一個(gè)對(duì)象上面, 還需要約束的屬性颓帝,關(guān)系大于米碰、等于窝革、小于,如果不是常量類型吕座,還需要另一個(gè)依賴的對(duì)象虐译,以及依賴的屬性,系數(shù)以及一個(gè)偏移常量吴趴。

這里的 make.center就是說添加到當(dāng)前漆诽,并設(shè)置約束屬性center,equalTo锣枝,則是表示關(guān)系為等于厢拭,self.view.snp.center則表示依賴的對(duì)象是 self.view,依賴的屬性也是 center惊橱,系數(shù)及偏移值這里均沒有指定蚪腐,表示使用默認(rèn)值。

public var center: ConstraintMakerExtendable {
        return self.makeExtendableWithAttributes(.center)
}

這個(gè)只是一個(gè)簡便方法税朴, 具體的實(shí)現(xiàn)繼續(xù)去查看定義

internal func makeExtendableWithAttributes(_ attributes: ConstraintAttributes) -> ConstraintMakerExtendable {
    let description = ConstraintDescription(item: self.item, attributes: attributes)
    self.descriptions.append(description)
    return ConstraintMakerExtendable(description)
}

流程為首先根據(jù)約束屬性及需要添加約束的對(duì)象生成一個(gè)描述,然后將其添加內(nèi)部的一個(gè)數(shù)組家制,也就是之前 makeConstraints中第一個(gè) for 循環(huán)鎖遍歷的數(shù)組正林,最后返回一個(gè) ConstraintMakerExtendable 對(duì)象。

  • ConstraintAttributes
internal struct ConstraintAttributes : OptionSet, ExpressibleByIntegerLiteral {
}

ConstraintAttributes 本身是一個(gè) OptionSet

public protocol OptionSet : RawRepresentable, SetAlgebra {
}
extension RawRepresentable where Self : Encodable, Self.RawValue == String {
    public func encode(to encoder: Encoder) throws
}
extension RawRepresentable where Self : Decodable, Self.RawValue == String {
    public init(from decoder: Decoder) throws
}

初始化颤殴,成為統(tǒng)一可操作的類型觅廓。

internal struct ConstraintAttributes : OptionSet {
    internal private(set) var rawValue: UInt internal init(rawValue: UInt) { self.rawValue = rawValue
    }
    internal static var left: ConstraintAttributes { 
        return self.init(1) 
    }
    internal static var top: ConstraintAttributes { 
        return self.init(2) 
    }
    internal static var right: ConstraintAttributes { 
        return self.init(4) 
    }
    ...這里有省略
    internal static var center: ConstraintAttributes { 
        return self.init(768) 
    }
}

ConstraintAttributes 本身是一個(gè) OptionSet,里面定義了許多屬性涵但, 例如 left, right, center使用 OptionSet 的意義在于杈绸,可以通過組合操作,同時(shí)添加多個(gè)屬性矮瘟,例如瞳脓,center這個(gè)屬性就是由 centerX 和 centerY 復(fù)合而來。

public class ConstraintDescription {
    internal let item: LayoutConstraintItem
    internal var attributes: ConstraintAttributes
    internal var relation: ConstraintRelation? = nil
    internal var sourceLocation: (String, UInt)? = nil
    internal var label: String? = nil
    internal var related: ConstraintItem? = nil        
    internal var multiplier: ConstraintMultiplierTarget = 1.0
    internal var constant: ConstraintConstantTarget = 0.0
    internal var priority: ConstraintPriorityTarget = 1000.0
    internal lazy var constraint: Constraint? =
    ...
    internal init(item: LayoutConstraintItem, attributes: ConstraintAttributes){
    self.item = item
    self.attributes = attributes
}

這個(gè)類是一個(gè)描述類澈侠,用于描述一條具體的約束劫侧,里面包含了約束的屬性,關(guān)系等回到ConstraintMaker.makeConstraints 中的第一個(gè) for 循環(huán)哨啃,里面就是去獲取description.constraint 已達(dá)到最終構(gòu)造約束的目的烧栋。

public class ConstraintMakerExtendable: ConstraintMakerRelatable {
   public var left: ConstraintMakerExtendable {
       self.description.attributes += .left
       return self
   } 
   ...
}

makeExtendableWithAttributes最后返回的時(shí)候, 返回的是一ConstraintMakerExtendable對(duì)象。這個(gè)類的主要目的是為了實(shí)現(xiàn)鏈?zhǔn)降亩鄬傩匀颍纾?code>make.center.equalTo(self.view.snp.center)這一句可以寫為审姓,make.centerX.centerY.equalTo(self.view.snp.center)

public func equalTo(_ other: ConstraintRelatableTarget, _ file: String = #file, _ line: UInt = #line) -> ConstraintMakerEditable {
     return self.relatedTo(other, relation: .equal, file: file, line: line)
}

ConstraintMakerExtendable 繼承自 ConstraintMakerRelatable,這個(gè)類主要是負(fù)責(zé)構(gòu)造一個(gè)關(guān)系祝峻,例如 equalTo

internal func relatedTo(_ other: ConstraintRelatableTarget, relation: ConstraintRelation, file: String, line: UInt) -> ConstraintMakerEditable {
         let related: ConstraintItem
         let constant: ConstraintConstantTarget
         if let other = other as? ConstraintItem {
              guard other.attributes == ConstraintAttributes.none ||
                         other.attributes.layoutAttributes.count <= 1 ||              
                   other.attributes.layoutAttributes == self.description.attributes.layoutAttributes ||  
                   other.attributes == .edges && self.description.attributes == .margins ||
                   other.attributes == .margins && self.description.attributes == .edges
              else { fatalError("Cannot constraint to multiple non identical attributes. (\(file), \(line))"); }
                 related = other constant = 0.0 }
             else if let other = other as? UIView {
                 related = ConstraintItem(target: other, attributes: ConstraintAttributes.none) constant = 0.0 }
             else if let other = other as? ConstraintConstantTarget {
                 related = ConstraintItem(target: nil, attributes: ConstraintAttributes.none) constant = other }
             else if #available(iOS 9.0, OSX 10.11, *), let other = other as? ConstraintLayoutGuide {
                related = ConstraintItem(target: other, attributes: ConstraintAttributes.none)
                constant = 0.0
          } else {
                fatalError(“Invalid constraint. (\(file), \(line))”)
          }
          let editable = ConstraintMakerEditable(self.description)             editable.description.sourceLocation = (file, line)
          editable.description.relation = relation
          editable.description.related = related        
          editable.description.constant = constant
          return editable
}         // equalTo 只是對(duì)內(nèi)部函數(shù)relatedTo 的一個(gè)簡單調(diào)用
public protocol ConstraintRelatableTarget {
}
extension Int: ConstraintRelatableTarget {
}
extension UInt: ConstraintRelatableTarget {
}
extension Float: ConstraintRelatableTarget {
}
extension ConstraintItem: ConstraintRelatableTarget {
}
extension ConstraintView: ConstraintRelatableTarget {
}

ConstraintRelatableTarget是一個(gè)協(xié)議魔吐,表示一個(gè)可以被依賴的目標(biāo)次坡,我們在手寫 NSLayoutConstraint 的時(shí)候,
依賴對(duì)象可以為 view画畅,可以為ConstraintLayoutGuide砸琅,也可以為空,為空的時(shí)候轴踱,表示使用絕對(duì)值症脂,該協(xié)議分別有 Int、 Double淫僻、CGPoint等字面值诱篷,也有UIView, ConstraintLayoutGuide雳灵,同時(shí)棕所,也有ConstraintItem,讓我們可以指定依賴的具體值, 我們之前的代碼 make.center.equalTo(self.view.snp.center)中的self.view.snp.center就是 ConstraintItem對(duì)象悯辙。

  • ConstraintItem

view.snp返回的是一個(gè) ConstraintViewDSL琳省,ConstraintViewDSL是繼承自 ConstraintAttributesDSL,而ConstraintAttributesDSL則是繼承自 ConstraintBasicAttributesDSL的ConstraintAttributesDSL與 ConstraintBasicAttributesDSL中定義了大量的布局屬性躲撰,如 top, bottom 等

public var center: ConstraintItem { return ConstraintItem(target: self.target, attributes: ConstraintAttributes.center) } …

其他均類似针贬。可以看到這里面構(gòu)造了一個(gè) ConstraintItem 對(duì)象:

public final class ConstraintItem {
    internal weak var target: AnyObject?
    internal let attributes: ConstraintAttributes
    internal init(target: AnyObject?, attributes: ConstraintAttributes) {
        self.target = target
        self.attributes = attributes
     }
     internal var layoutConstraintItem: LayoutConstraintItem? {
          return self.target as? LayoutConstraintItem
     }
}
  • ConstraintMakerEditable

ConstraintMakerEditable 這個(gè)類主要是設(shè)置Autolayout 中的兩個(gè)常量multiplier 和 constant 與優(yōu)先級(jí)拢蛋,使用方法如make.center.equalTo(self.view.snp.center).offset(20)

再次回到makeConstraints桦他,通過上面的若干步驟,完成了對(duì) ConstraintDescription的設(shè)置谆棱,現(xiàn)在可以用他來生成 Constraint了快压,生成的部分在ConstraintDescription 的 constraint 屬性里面

internal lazy var constraint: Constraint? = {
    guard let relation = self.relation,
    let related = self.related,
    let sourceLocation = self.sourceLocation else {
         return nil
    }
    let from = ConstraintItem(target: self.item, attributes: self.attributes)
         return Constraint(
              from: from,
              to: related,
              relation: relation,
              sourceLocation: sourceLocation,
              label: self.label,
              multiplier: self.multiplier,
              constant: self.constant,
              priority: self.priority )
    }()

Constraint 創(chuàng)建過程很像NSLayoutConstraint
Constraint這個(gè)類主要就是生成和操縱 NSLayoutConstraint。構(gòu)造函數(shù)有點(diǎn)長垃瞧,下面是去掉一些簡單的賦值和多平臺(tái)適配后的代碼

internal init(...) {
    self.layoutConstraints = []
    // get attributes
    let layoutFromAttributes = self.from.attributes.layoutAttributes
    let layoutToAttributes = self.to.attributes.layoutAttributes
    // get layout from
    let layoutFrom = self.from.layoutConstraintItem!
    // get relation
    let layoutRelation = self.relation.layoutRelation
    ……

函數(shù)中第一行的self.layoutConstraints = []使用來存放所有最后生成的NSLayoutConstraint
后面的兩行是獲取兩個(gè)對(duì)象的約束屬性蔫劣。而 layoutFrom則是約束屬性的起始對(duì)象,在我們最初那段代碼中皆警,就表示了snplabel這個(gè)視圖拦宣。

for layoutFromAttribute in layoutFromAttributes {
    // get layout to attribute
    let layoutToAttribute: NSLayoutAttribute
        if layoutToAttributes.count > 0 {
            if self.from.attributes == .edges && self.to.attributes == .margins {
                 switch layoutFromAttribute {
                      case .left: layoutToAttribute = .leftMargin
                      case .right: layoutToAttribute = .rightMargin
                      case .top: layoutToAttribute = .topMargin
                      case .bottom: layoutToAttribute = .bottomMargin
                      default: fatalError()
                 }
             } else if self.from.attributes == .margins && self.to.attributes == .edges {
                 switch layoutFromAttribute {
                     case .leftMargin: layoutToAttribute = .left
                     case .rightMargin: layoutToAttribute = .right
                     case .topMargin: layoutToAttribute = .top
                     case .bottomMargin: layoutToAttribute = .bottom
                     default: fatalError()
                 }
             } else if self.from.attributes == self.to.attributes {
                  layoutToAttribute = layoutFromAttribute } else {
                      layoutToAttribute = layoutToAttributes[0]
                  }
           } else {
                 if self.to.target == nil && (layoutFromAttribute == .centerX || layoutFromAttribute == .centerY) {
                      layoutToAttribute = layoutFromAttribute == .centerX ? .left : .top
                } else {
                      layoutToAttribute = layoutFromAttribute
                }
          }
          // get layout constant
          let layoutConstant: CGFloat = self.constant.constraintConstantTargetValueFor(layoutAttribute: layoutToAttribute)
          // get layout to
          var layoutTo: AnyObject? = self.to.target
          // use superview if possible 
          if layoutTo == nil && layoutToAttribute != .width && layoutToAttribute != .height { layoutTo = layoutFrom.superview }
          // create layout constraint
          let layoutConstraint = LayoutConstraint( item: layoutFrom, attribute: layoutFromAttribute, relatedBy: layoutRelation, toItem: layoutTo, attribute: layoutToAttribute, multiplier: self.multiplier.constraintMultiplierTargetValue, constant: layoutConstant )
           // set label layoutConstraint.label = self.label
           // set priority layoutConstraint.priority = self.priority.constraintPriorityTargetValue
           // set constraint layoutConstraint.constraint = self
           // append self.layoutConstraints.append(layoutConstraint)
      }
}

后面則是獲取約束的關(guān)系, 如等于, 大于。主要的代碼都在那個(gè)循環(huán)中信姓,主要邏輯是遍歷添加在起始對(duì)象上的約束屬性鸵隧,然后獲取預(yù)支對(duì)應(yīng)的目標(biāo)對(duì)象及目標(biāo)對(duì)象的約束屬性,最后生成LayoutConstraint

其中第一個(gè) if else 分支中在確定目標(biāo)屬性該使用何種值, 通過分析可以看出, 我們之前那段代碼, 其實(shí)可以將make.center.equalTo(self.view.snp.center)中直接寫為make.center.equalTo(self.view)

后面則是根據(jù)不同的目標(biāo)屬性意推,獲取適當(dāng)?shù)钠浦刀固薄R约矮@取目標(biāo)對(duì)象。
后面 LayoutConstraint(xxx) 中的 LayoutConstraint 其實(shí)只是一個(gè)NSLayoutConstraint 的子類菊值,只是在其中添加了一個(gè)標(biāo)簽與創(chuàng)建者(Constraint) 的引用

  • activateIfNeeded

makeConstraints最后一步則是激活, 在 iOS 8 以前外驱, 所有的依賴屬性育灸, 都必須使用 view.addConstraint(xxx)方法將依賴激活, iOS 8 后昵宇, 則直接將依賴激活即可生效磅崭。activateIfNeeded 則是將依賴激活使其生效

SnapKit 源碼結(jié)構(gòu)圖

結(jié)構(gòu)圖.png

SnapKit 源碼類圖

類圖.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市瓦哎,隨后出現(xiàn)的幾起案子砸喻,更是在濱河造成了極大的恐慌,老刑警劉巖蒋譬,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件割岛,死亡現(xiàn)場離奇詭異,居然都是意外死亡犯助,警方通過查閱死者的電腦和手機(jī)癣漆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來剂买,“玉大人惠爽,你說我怎么就攤上這事±资眩” “怎么了疆股?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長倒槐。 經(jīng)常有香客問我,道長附井,這世上最難降的妖魔是什么讨越? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮永毅,結(jié)果婚禮上把跨,老公的妹妹穿的比我還像新娘。我一直安慰自己沼死,他們只是感情好着逐,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著意蛀,像睡著了一般耸别。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上县钥,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天秀姐,我揣著相機(jī)與錄音,去河邊找鬼若贮。 笑死省有,一個(gè)胖子當(dāng)著我的面吹牛痒留,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蠢沿,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼伸头,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了舷蟀?” 一聲冷哼從身側(cè)響起恤磷,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎雪侥,沒想到半個(gè)月后碗殷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡速缨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年宋光,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片断部。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡呢燥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出原茅,到底是詐尸還是另有隱情吭历,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布擂橘,位于F島的核電站晌区,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏通贞。R本人自食惡果不足惜朗若,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望昌罩。 院中可真熱鬧哭懈,春花似錦、人聲如沸茎用。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽轨功。三九已至旭斥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間夯辖,已是汗流浹背琉预。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蒿褂,地道東北人圆米。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓卒暂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親娄帖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子也祠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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