Swift style guide

Swift style guide.

Introduction

API Design Guidelines是苹果专门针对API的一个规范,本规范涉及到一些相关的内容,大都是保持和苹果的规范一致。

它绝大部分内容,集合了The Official raywenderlich.com Swift Style Guide.GitHub's Swift Style Guide,并删减了一些不适合编码的规则。

同时,Swift语言在快速的发展中,这个规范也会随着Swift的发展、以及对Swift更多的使用和了解,持续地进行修改和完善。

Table of Contents

<h2 id="correctness"> Correctness </h2>

在Swift中把warnings当做errors。该规则会解决掉很多其他的代码格式化规则,比如不要使用 ++--操作符、不要使用C类型的for循环、不要直接使用字符串作为selector等。

<h2 id="naming"> Naming </h2>

classes, structures, enumerations 和 protocols采用首字母写的驼峰命名法,method names and variables采用首字母写的驼峰命名法。

Preferred:

swift
private let maximumWidgetCount = 100

class WidgetContainer {
  var widgetButton: UIButton
  let widgetHeightPercentage = 0.85
}

Not Preferred:

swift
let MAX_WIDGET_COUNT = 100

class app_widgetContainer {
  var wBut: UIButton
  let wHeightPct = 0.85
}

缩写应该避免,但如URL、ID这种常见的缩写可以使用。

API Design Guidelines中提到,如果使用这些缩写,字母应该全为大写或者小写。Examples:

Preferred

let urlString: URLString
let userID: UserID

Not Preferred

let uRLString: UrlString
let userId: UserId

使用argument label,替代注释,使代码self-documenting,可读性更强。

swift
func convertPointAt(column: Int, row: Int) -> CGPoint
func timedAction(afterDelay delay: NSTimeInterval, perform action: SKAction) -> SKAction!

// would be called like this:
convertPointAt(column: 42, row: 13)
timedAction(afterDelay: 1.0, perform: someOtherAction)

<h3 id="protocols"> Protocols </h3>

按照苹果的API Design Guidelines,Protocols名字可以使用名词来描述这个Protocol的内容,比如Collection, WidgetFactory

或以-ing、-able结尾来描述Protocol实现的一些功能,比如: Equatable, Resizing

<h3 id="enumerations"> Enumerations </h3>

按照苹果的API Design Guidelines,枚举值使用小写开头的驼峰命名法,也就是lowerCamelCase。

swift
enum Shape {
  case rectangle
  case square
  case rightTriangle
  case equilateralTriangle
}

<h3 id="class-prefixes"> Class Prefixes </h3>

在Swift里面,每一个module都是一个namesapce。而在ObjC里没有namespace的概念,只是在每个类名前面添加前缀,比如NSArray。

当不同的module有同名的类名时,需要指明module name。

swift
import SomeModule

let myClass = MyModule.UsefulClass()

<h3 id="selectors"> Selectors </h3>

Selectors是在ObjC中为许多Cocoa and Cocoa Touch APIs做处理的函数。在Swift2.2,我们可以使用类型不安全的字符串来指定一个Selector。但在Swift3,这种方式将使Xcode报一个警告,在警告的"Fix it"按钮里面,会使用完全类型安全的方式去替换这个不安全的字符串。并且我们经常能够使用代码所在的上下文来简化Swift3中Selector的表达式。

Preferred:

swift
let sel = #selector(viewDidLoad)

Not Preferred:


swift
let sel = #selector(ViewController.viewDidLoad)

<h3 id="generics"> Generics </h3>

范型的类型名,应该是描述性的名词、以大写开头的驼峰命名。如果不能起一个有意义的关系或角色名称,可以使用TUV

Preferred:

swift
struct Stack<Element> { ... }
func writeTo<Target: OutputStream>(inout target: Target)
func max<T: Comparable>(x: T, _ y: T) -> T

Not Preferred:

swift
struct Stack<T> { ... }
func writeTo<target: OutputStream>(inout t: target)
func max<Thing: Comparable>(x: Thing, _ y: Thing) -> Thing

<h3 id="language"> Language </h3>

为了和苹果的API匹配,使用美式英语。

Preferred:

swift
let color = "red"

Not Preferred:

swift
let colour = "red"

<h2 id="code-organization"> Code Organization </h2>

多使用extension来组织代码。每个 extensions使用// MARK: -来分隔开。

<h3 id="protocol-conformance"> Protocol Conformance </h3>

当一个类遵守一个协议时,使用extension来组织代码。

Preferred:

swift
class MyViewcontroller: UIViewController {
  // class stuff here
}

// MARK: - UITableViewDataSource
extension MyViewcontroller: UITableViewDataSource {
  // table view data source methods
}

// MARK: - UIScrollViewDelegate
extension MyViewcontroller: UIScrollViewDelegate {
  // scroll view delegate methods
}

Not Preferred:

swift
class MyViewcontroller: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
  // all methods
}

<h3 id="unused-code"> Unused Code </h3>

能删除的代码,都删掉。

Not Preferred:

swift
override func didReceiveMemoryWarning() {
   super.didReceiveMemoryWarning()
  // Dispose of any resources that can be recreated.
}

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
   // #warning Incomplete implementation, return the number of sections
   return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  // #warning Incomplete implementation, return the number of rows
  return Database.contacts.count
}

Preferred:

swift
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  return Database.contacts.count
}

<h3 id="minimal-imports"> Minimal Imports </h3>

尽可能减少依赖和import。比如只需要引入Foundation的时候,就不要引入UIKit。

<h2 id="spacing"> Spacing </h2>

  • 缩进使用4个空格(该处只要团队统一就行):
  • 方法体的花括号和其它的花括号(if/else/switch/while etc.),需要加入一个空格后在行尾开启,在新一行关闭(Xcode默认)。
  • 提示:⌘A选中代码后使用Control-I (或者菜单Editor\Structure\Re-Indent)来调整缩进.

Preferred:

swift
if user.isHappy {
  // Do something
} else {
  // Do something else
}

Not Preferred:

swift
if user.isHappy
{
  // Do something
}
else {
  // Do something else
}
  • methods之间只留一个空行。methods内部,使用空行来分隔不同功能的代码,为不同的section。一个method内部的section不宜太多,否则应该考虑分拆成不同函数。
  • 冒号左边没有空格,右边有一个空格。Exception:? :[:]

Preferred:

swift
class TestDatabase: Database {
  var data: [String: CGFloat] = ["A": 1.2, "B": 3.2]
}

Not Preferred:

swift
class TestDatabase : Database {
  var data :[String:CGFloat] = ["A" : 1.2, "B":3.2]
}

<h2 id="comments"> Comments </h2>

尽可能避免大量使用注释,好的代码应该尽可能是self-documenting。

如果需要注释,它只用来解释为什么这段代码要这么写,而不是解释代码的逻辑。
代码变化时也需要马上更新注释,不能有误导。如果不能及时更新就删掉该处注释。

Exception: 上面两条不适用于生成文档用的注释.

<h2 id="classes-and-structures"> Classes and Structures </h2>

应该使用哪一个?

记住,struct具有值语义。没有id(唯一标识符)的事物就应该使用struct。比如一个含有[a, b, c]的数组和另一个含有[a, b, c]的数组是完全可交换的。你使用第一个数组还是第二个完全没有关系,因为他们代表同一个事物,这也是为什么在Swift里面数组是用struct结构来表示的。

而类具有引用语义。具有id(唯一标识符)或者具有特定生命周期的事物就应该使用类来表示。你将使用类来表示人的数据结构,因为两个人完全是不同的事物,只是因为两个人具有相同名字和生日,并不意味着这是相同的一个人。但是一个人的生日可以使用struct来表示,因为一个变量中的1950年3月3号和另一个的1950年3月3号是完全相同的。日期是不具有id的。

但有些时候,一个事物应该是struct的,但需要遵循AnyObject或者由于历史原因被模型化为类了(NSDate, NSSet)。除了这些异常情况,尽量遵循该条原则。

<h3 id="example-definition"> Example definition </h3>

下面是一个比较规范的Class定义:

swift
class Circle: Shape {
  var x: Int, y: Int
  var radius: Double
  var diameter: Double {
    get {
      return radius * 2
    }
    set {
      radius = newValue / 2
    }
  }

  init(x: Int, y: Int, radius: Double) {
    self.x = x
    self.y = y
    self.radius = radius
  }

  convenience init(x: Int, y: Int, diameter: Double) {
    self.init(x: x, y: y, radius: diameter / 2)
  }

  func describe() -> String {
    return "I am a circle at \(centerString()) with an area of \(computeArea())"
  }

  override func computeArea() -> Double {
    return M_PI * radius * radius
  }

  private func centerString() -> String {
    return "(\(x),\(y))"
  }
}

上面的例子,给我们演示了这些规范:

  • 冒号在用于指明类型时,左边没有空格,右边有一个空格。
  • 当一组变量、常量有关联时,定义在一行里。
  • 函数修饰符internal是缺省值,可以省略。重载一个函数时,访问修饰符也可以省略掉。

<h3 id="use-of-self"> Use of Self </h3>

避免使用self来访问属性。除非需要区分函数参数和属性。

swift
class BoardLocation {
  let row: Int, column: Int

  init(row: Int, column: Int) {
    self.row = row
    self.column = column
    
    let closure = {
      print(self.row)
    }
  }
}

<h3 id="computed-properties"> Computed Properties </h3>

Computed property一般是只读,同时省略get clause。get clause只是当set clause存在时才需要写。

Preferred:

swift
var diameter: Double {
  return radius * 2
}

Not Preferred:

swift
var diameter: Double {
  get {
    return radius * 2
  }
}

<h3 id="final"> Final </h3>

当一个类不想被继承时,使用final关键字。Example:

swift
// Turn any generic type into a reference type using this Box class.
final class Box<T> {
  let value: T 
  init(_ value: T) {
    self.value = value
  }
}

<h2 id="function-declarations"> Function Declarations </h2>

在一行中保持简短的函数声明,函数体的左方括号也需要放在这一行。

swift
func reticulateSplines(spline: [Double]) -> Bool {
  // reticulate code goes here
}

如果一个函数签名过长,则选择合适的地方换行,并在新的一行加入足够的缩进。

swift
func reticulateSplines(spline: [Double], adjustmentFactor: Double,
    translateConstant: Int, comment: String) -> Bool {
  // reticulate code goes here
}

<h2 id="closure-expressions"> Closure Expressions </h2>

方法的参数列表最后一参数类型为闭包时,可以使用尾闭包。但只在只存在一个闭包参数时才使用尾闭包。

Preferred:

swift
UIView.animateWithDuration(1.0) {
  self.myView.alpha = 0
}

UIView.animateWithDuration(1.0,
  animations: {
    self.myView.alpha = 0
  },
  completion: { finished in
    self.myView.removeFromSuperview()
  }
)

Not Preferred:

swift
UIView.animateWithDuration(1.0, animations: {
  self.myView.alpha = 0
})

UIView.animateWithDuration(1.0,
  animations: {
    self.myView.alpha = 0
  }) { f in
    self.myView.removeFromSuperview()
}

只有一个表达式的、用来返回值的闭包,可以省略return。

swift
attendeeList.sort { a, b in
  a > b
}

<h2 id="types"> Types </h2>

尽可能使用Swift原生类型,而不是使用ObjC的NS类型。

Preferred:

swift
let width = 120.0                                    // Double
let widthString = (width as NSNumber).stringValue    // String

Not Preferred:

swift
let width: NSNumber = 120.0                          // NSNumber
let widthString: NSString = width.stringValue        // NSString

但是有些情况例外,比如在写Sprite Kit代码时,用CGFloat可以减少转换。

<h3 id="constants"> constants </h3>

尽可能使用let,只有在需要使用变量的时候使用var。

Tip: 有一个办法能达到上面的目的,就是自己只写let,让编译器帮你确定哪些需要改成var。

使用static let定义类常量,而不是实例常量,或者全局常量。

Preferred:

swift
enum Math {
  static let e  = 2.718281828459045235360287
  static let pi = 3.141592653589793238462643
}

radius * Math.pi * 2 // circumference

Note: 使用枚举定义常量的好处是让常量定义在特定的命名空间。

Not Preferred:

swift
let e  = 2.718281828459045235360287  // pollutes global namespace
let pi = 3.141592653589793238462643

radius * pi * 2 // is pi instance data or a global constant?

<h3 id="optionals"> Optionals </h3>

当访问一个optional value前,需要访问多个optional value时,使用optional chaining:

swift
self.textContainer?.textLabel?.setNeedsDisplay()

访问一个optional value后,需要执行多个操作,可以使用optional binding:

swift
if let textContainer = self.textContainer {
  // do many things with textContainer
}

给optional变量命名时,不要使用类似optionalStringmaybeView这样的命名,因为optional已经在类型声明中体现。

相对应的,使用unwrapped value时,避免使用unwrappedViewactualLabel这样的命名,使用optional变量名就可以了。

Preferred:

swift
var subview: UIView?
var volume: Double?

// later on...
if let subview = subview, volume = volume {
  // do something with unwrapped subview and volume
}

Not Preferred:

swift
var optionalSubview: UIView?
var volume: Double?

if let unwrappedSubview = optionalSubview {
  if let realVolume = volume {
    // do something with unwrappedSubview and realVolume
  }
}

避免使用可选值的强制解析

如果你有一个变量 fooFooType? 或者 FooType!类型,如果可能,尽量不要使用强制解析来获取 foo真正的值。

Preferred:

swift
if let foo = foo {
    // Use unwrapped `foo` value in here
} else {
    // If appropriate, handle the case where the optional is nil
}

或者,在一些情况下你可以使用可选链,例如:

swift
// Call the function if `foo` is not nil. If `foo` is nil, ignore we ever tried to make the call
foo?.callSomethingIfFooIsNotNil()

显示的 if let可选绑定是安全的代码范式。强制解析常常会引起运行时的崩溃。

避免使用隐士强制解析

如果 foo可以是nil, 尽量使用 let foo: FooType?这种范式,而不要使用let foo: FooType! (一般来说,使用!的地方都可以使用?)

显示的可选类型是安全的代码范式。隐士强制解析有可能引起运行时的崩溃。

<h3 id="struct-initializers"> Struct Initializers </h3>

使用Swfit原生的struct initializers,而不是遗留的CGGeometry constructors。

Preferred:

swift
let bounds = CGRect(x: 40, y: 20, width: 120, height: 80)
let centerPoint = CGPoint(x: 96, y: 42)

Not Preferred:

swift
let bounds = CGRectMake(40, 20, 120, 80)
let centerPoint = CGPointMake(96, 42)

同样的,使用struct-scope constants CGRect.infiniteCGRect.null, etc, 而不是global constants CGRectInfinite, CGRectNull, etc。
你也可以使用.zero来给变量赋值,如var frame = CGRect.zero。对于一个已经存在的变量也可以这样bounds = .zero

<h3 id="lazy-initialization"> Lazy Initialization </h3>

合适的时候考虑使用lazy加载来更好的控制一个对象的生命周期。这对UIViewController懒加载view特别有用。你可以使用一个形如 { }() 的立即调用的闭包,或者调用一个private的工厂函数。例如:

swift
lazy var locationManager: CLLocationManager = self.makeLocationManager()

private func makeLocationManager() -> CLLocationManager {
  let manager = CLLocationManager()
  manager.desiredAccuracy = kCLLocationAccuracyBest
  manager.delegate = self
  manager.requestAlwaysAuthorization()
  return manager
}

Notes:

  • [unowned self] 在这里不需要,这里不会产生循环引用。
  • 因为Location manager会弹出弹框询问用户定位的权限,所以这里控制它的生命周期很有意义。

<h3 id="type-inference"> Type Inference </h3>

尽量让编译器使用类型推断来标示常量或者变量的类型,这样可以简短代码。类型推断也适用于小数据量(不为空)的数组和字典。只有当需要的时候,才指明类型,如CGFloat or Int16

Preferred:

swift
let message = "Click the button"
let currentBounds = computeViewBounds()
var names = ["Mic", "Sam", "Christine"]
let maximumWidth: CGFloat = 106.5

Not Preferred:

swift
let message: String = "Click the button"
let currentBounds: CGRect = computeViewBounds()
let names = [String]()

空数组或者字典的类型标注

对于空的数组、字典需要显示使用类型标注。对于初始化的时候使用大量、对方数组、字典常量初始化的情况也需要显示使用类型标注。

Preferred:

swift
var names: [String] = []
var lookup: [String: Int] = [:]

Not Preferred:

swift
var names = [String]()
var lookup = [String: Int]()

NOTE: 准守这条规则意味着取一个描述性的名称显示更加重要。

<h3 id="syntactic-sugar"> Syntactic Sugar </h3>

尽可能使用语法糖。

Preferred:

swift
var deviceModels: [String]
var employees: [Int: String]
var faxNumber: Int?

Not Preferred:

swift
var deviceModels: Array<String>
var employees: Dictionary<Int, String>
var faxNumber: Optional<Int>

<h2 id="functions-vs-methods"> Functions vs Methods </h2>

不依附于任何class或type的函数被称为free function,应该尽量避免使用,因为不太好找到这个方法。

Preferred

swift
let sorted = items.mergeSort()  // easily discoverable
rocket.launch()  // clearly acts on the model

Not Preferred

swift
let sorted = mergeSort(items)  // hard to discover
launch(&rocket)

Free Function Exceptions(下面的函数明确应该是fress function,并且比较好理解)

swift
let tuples = zip(a, b)  // feels natural as a free function (symmetry)
let value = max(x,y,z)  // another free function that feels natural

<h2 id="memory-management"> Memory Management </h2>

代码应该避免参数循环引用。分析你的对象引用视图,并使用weakunowned避免不必要的强引用。同时可以选择使用值类型(struct, enum)来避免循环引用。

扩展一个对象的生命周期

可以使用 [weak self]guard let strongSelf = self else { return } 组合的方式来扩展一个对象的生命周期。当 self 明显生命周期比使用它的闭包更长的时候,最好选择使用 [unowned self]

Preferred

swift
resource.request().onComplete { [weak self] response in
  guard let strongSelf = self else { 
    return 
  }
  let model = strongSelf.updateModel(response)
  strongSelf.updateUI(model)
}

Not Preferred

swift
// might crash if self is released before response returns
resource.request().onComplete { [unowned self] response in
  let model = self.updateModel(response)
  self.updateUI(model)
}

Not Preferred

swift
// deallocate could happen between updating the model and updating UI
resource.request().onComplete { [weak self] response in
  let model = self?.updateModel(response)
  self?.updateUI(model)
}

<h2 id="access-control"> Access Control </h2>

访问修饰符应该放在靠前的位置,前面只能有static@IBAction@IBOutlet

Preferred:

swift
class TimeMachine {  
  private dynamic lazy var fluxCapacitor = FluxCapacitor()
}

Not Preferred:

swift
class TimeMachine {  
  lazy dynamic private var fluxCapacitor = FluxCapacitor()
}

对于全局的定义,一点显示的标示访问修饰符

顶层的函数、类型、变量总是应该显示的指明访问修饰符:

swift
public var whoopsGlobalState: Int
internal struct TheFez {}
private func doTheThings(things: [Thing]) {}

但是对于不少顶层的声明,如果可以尽量不要显示指定访问修饰符:

swift
internal struct TheFez {
    var owner: Person = Joshaber()
}

对于顶层的(全局的)声明很少有被指定为 internal 的情况,显示的指明访问修饰符可以保证这个顶层声明的设计是被充分考虑过的。但在这些声明里面,使用已有的访问修饰符是更好的方式。

<h2 id="control-flow"> Control Flow </h2>

相比于 while-condition-increment 格式的循环,请尽量使用 for-in 这种方式。

Preferred:

swift
for _ in 0..<3 {
  print("Hello three times")
}

for (index, person) in attendeeList.enumerate() {
  print("\(person) is at position #\(index)")
}

for index in 0.stride(to: items.count, by: 2) {
  print(index)
}

for index in (0...3).reverse() {
  print(index)
}

Not Preferred:

swift
var i = 0
while i < 3 {
  print("Hello three times")
  i += 1
}


var i = 0
while i < attendeeList.count {
  let person = attendeeList[i]
  print("\(person) is at position #\(i)")
  i += 1
}

<h2 id="golden-path"> Golden Path </h2>

嵌套的if会让代码的缩进层次不齐(整齐的缩进被称作Golden Path),会让代码可读性变差,使用guard来做函数输入合法性检查,可以减少if嵌套。

Preferred:

swift
func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies {

  guard let context = context else { 
    throw FFTError.noContext 
  }
  guard let inputData = inputData else { 
    throw FFTError.noInputData 
  }
    
  // use context and input to compute the frequencies
    
  return frequencies
}

Not Preferred:

swift
func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies {

  if let context = context {
    if let inputData = inputData {
      // use context and input to compute the frequencies

      return frequencies
    }
    else {
      throw FFTError.noInputData
    }
  }
  else {
    throw FFTError.noContext
  }
}

Preferred:

swift
guard let number1 = number1, 
        number2 = number2, 
        number3 = number3 else { 
        fatalError("impossible") 
}
// do something with numbers

Not Preferred:

swift
if let number1 = number1 {
  if let number2 = number2 {
    if let number3 = number3 {
      // do something with numbers
    }
    else {
      fatalError("impossible")
    }
  }
  else {
    fatalError("impossible")
  }
}
else {
  fatalError("impossible")
}

<h3 id="failing-guards"> Failing Guards </h3>

Guard检查失败执行的语句应该退出当前方法,并且应该只有一条语句,如return, throw, break, continue, and fatalError()。如果需要多行语句,考虑使用defer

<h2 id="semicolons"> Semicolons </h2>

Swift并不要求每行语句都必须以冒号结束,除了你需要在一行写多个语句的情况下。

建议不要在一行写多个语句。

这条规则的唯一例外是for-conditional-increment语句。但是我们推荐使用for-in来代替传统的for语句。

Preferred:

swift
let swift = "not a scripting language"

Not Preferred:

swift
let swift = "not a scripting language";

NOTE: Swift不像JavaScript那样认为省略掉句尾的冒号是不安全的

<h2 id="parentheses"> Parentheses </h2>

包住条件语句的圆括号应该省略。

Preferred:

swift
if name == "Hello" {
  print("World")
}

Not Preferred:

swift
if (name == "Hello") {
  print("World")
}

<h2 id="use-whitespace-around-operator-definitions"> Use whitespace around operator definitions </h2>

在自定义操作符的时候,使用空格作为分割符:

Preferred:

swift
func <| (lhs: Int, rhs: Int) -> Int
func <|< <A>(lhs: A, rhs: A) -> A

Not Preferred:

swift
func <|(lhs: Int, rhs: Int) -> Int
func <|<<A>(lhs: A, rhs: A) -> A

如果构成操作符的字符后面立即跟随类型或者参数的字符,将使其可读性变差。加入合适的空格将让它看起来更加清晰。

<h2 id="和xcode集成的格式检查工具"> 和Xcode集成的格式检查工具 </h2>
SwiftLint是开源社区贡献的一个Swift格式检查工具,可以较好的和Xcode集成,并提供warning、errors和修复提示的工具。它使用的规则基本遵循GitHub's Swift Style Guide,是团队很好的统一格式的一个辅助工具。

<h2 id="document-revision-history"> Document Revision History </h2>

  • 2016-11-11 Created by xdyang
  • 2016-11-30 修改搜索跳转方式
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容

  • Introduction 之前有一个Objective-C style guide.,这一篇是针对swift的一个...
    小山Sam阅读 1,926评论 0 8
  • 这是 Linkedin Swift 的代码规范,我挑出了我懂的地方进行翻译。原文 代码格式 一个 tab 四行 一...
    美航球馆阅读 422评论 0 0
  • 136.泛型 泛型代码让你可以写出灵活,可重用的函数和类型,它们可以使用任何类型,受你定义的需求的约束。你可以写出...
    无沣阅读 1,456评论 0 4
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young阅读 3,788评论 1 10
  • 这张照片,在10年前既是我的电脑桌面也是我的手机桌面和屏保,并且一下保持了很多年直到结婚,不忍放手。到如今分手已过...
    不知道是谁最好阅读 401评论 0 1