Swift 编码规范(中文)

注:以下皆为翻译,如有错误或疏漏,请指正。谢谢☺

简介

(raywenderlich 版)
您看到的这份规范可能与其他的有所不同,因为这份关注点是web显示和打印。我们创建这份规范已保证我们书中的代码,教程和 sdk 一致。

Objective-C 代码规范:Objective-C Style Guide.

Table of Contents(目录)

<a id="correctness"></a>

Correctness(正确性)

将 warnings 视为 errors. 比如不要用 ++ 、-- 或 c 风格的 循环 或 string 型的 selectors.

<a id="naming"></a>

Naming(命名)

classes, structures, enumerations and protocols 等类型名字以首字母大写驼峰命名。变量、方法名以小写驼峰方式命名

Preferred(推荐):

private let maximumWidgetCount = 100

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

Not Preferred(不推荐):

let MAX_WIDGET_COUNT = 100

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

尽量避免“简称”和“缩略词”,通用缩略词应该整体大写整体小写

Preferred(推荐)

let urlString: URLString
let userID: UserID

Not Preferred(不推荐)

let uRLString: UrlString
let userId: UserId

初始化方法或其他方法,每个参数前都应该有明确说明。

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

// 调用方式:
dateFromString("2014-03-14")
convertPointAt(column: 42, row: 13)
timedAction(afterDelay: 1.0, perform: someOtherAction)

遵循 Apple 官方习俗为方法命名。

class Counter {
  func combineWith(otherCounter: Counter, options: Dictionary?) { ... }
  func incrementBy(amount: Int) { ... }
}

<a id="protocols"></a>

Protocols(委托)

遵循Apple's API Design Guidelines。 protocols描述某物的应该是名词。如:Collection, WidgetFactory
protocols是描述能力的应该以-ing, -able或 -ible结尾。如: Equatable, Resizing

<a id="enumerations"></a>

Enumerations(枚举)

遵循 swift 3 Apple's API Design Guidelines。每个枚举值用小写字母开始。

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

<a id="prose"></a>

Prose

When referring to functions in prose (tutorials, books, comments) include the required parameter names from the caller's perspective or _ for unnamed parameters. Examples:

Call convertPointAt(column:row:) from your own init implementation.

If you call dateFromString(_:) make sure that you provide a string with the format "yyyy-MM-dd".

If you call timedAction(afterDelay:perform:) from viewDidLoad() remember to provide an adjusted delay value and an action to perform.

You shouldn't call the data source method tableView(_:cellForRowAtIndexPath:) directly.

This is the same as the #selector syntax. When in doubt, look at how Xcode lists the method in the jump bar – our style here matches that.

Methods in Xcode jump bar
Methods in Xcode jump bar

<a id="class-prefixes"></a>

Class Prefixes(Class 前缀)

swift 被自动包含了 module 前缀,你不需要添加额外前缀。如果两个不同的 module 的两个名字有冲突,你可以通过在 module 前添加前缀来消除该冲突。当然,仅当 module 名称可能冲突的时候才为他特定设置一个名称(这种情况应该很少见)。

import SomeModule

let myClass = MyModule.UsefulClass()

<a id="selectors"></a>

Selectors(选择器)

Preferred(推荐):

let sel = #selector(viewDidLoad)

Not Preferred(不推荐):

let sel = #selector(ViewController.viewDidLoad)

<a id="generics"></a>

Generics(泛型)

泛型参数应该描述清楚所规定的泛型。当不确定泛型类型是才使用传统的大写字母 如T, U, or V表示泛型。

Preferred(推荐):

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

Not Preferred(不推荐):

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

<a id="language"></a>

Language(语言)

用美式英语拼写才符合Apple's API

Preferred(推荐):

let color = "red"

Not Preferred(不推荐):

let colour = "red"

<a id="code-organization"></a>

Code Organization(代码组织)

Use extensions to organize your code into logical blocks of functionality. Each extension should be set off with a // MARK: - comment to keep things well-organized.

<a id="protocol-conformance"></a>

Protocol Conformance

每个 protocol 的实现分别对应一个 extension 的方式实现。

Preferred(推荐):

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(不推荐):

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

由于编译器不允许一个类重新申明 protocol 实现,因此,不是一直要求每个 protocols 都要分离出一个 extension.特别是当该 class 是个最终 class 或该 class 方法很少。

对UIKit view controllers,将lifecycle, custom accessors, and IBAction等方法单独放在一个 class extension 中。

<a id="unused-code"></a>

Unused Code(无用代码)

无用的code ,包括 Xcode 注释都应该被移除。空方法应该被移除。

Not Preferred(不推荐):

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(推荐):

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

<a id="minimal-imports"></a>

Minimal Imports(最少引入)

不要引用UIKitFoundation

<a id="spacing"></a>

Spacing(空格)

  • 用4个空格而不是 tabs调整对其,xcode 中设置如下:
Xcode indent settings
Xcode indent settings
Xcode Project settings
Xcode Project settings
  • 用命令Ctr+I重新排列代码。

Preferred(推荐):

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

Not Preferred(不推荐):

if user.isHappy
{
  // Do something
}
else {
  // Do something else
}
  • 每两个方法之间应该空一行。

  • 冒号左边无空格,右边有一个空格。? : 和 空字典[:] 例外

Preferred(推荐):

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

Not Preferred(不推荐):

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

<a id="comments"></a>

Comments(注释)

必要的时候添加注释注明 为什么

避免一大段注释解释一行代码,代码应该有自我解释性Exception: This does not apply to those comments used to generate documentation.

<a id="classes-and-structures"></a>

Classes and Structures(类和结构体)

<a id="which-one-to-use"></a>

用哪一个?

struct 是值类型,当你不需要唯一身份的时候用struct.arrayOne = [a, b, c] 和arrayTwo = [a, b, c]意义是一样的。不需要在乎他们是否是同一个 array。这就是用 struct 的原因。

class 是引用类型。需要唯一标识或有生命周期的才用 class。你可以把一个人定义为一个 class 实例,因为每个人都是不一样的。仅仅只有名字和生日相同的两人也不是同一个人。

有时候,应该是 struct 确是 class ,如以下情况 NSDate NSSet

<a id="example-definition"></a>

Example Definition(示例)

以下是一个好的 class 代码示范:

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))"
  }
}

以上示例演示遵循如下指导:

  • 声明的类型用: 衔接在后面
  • 多个变量公用 关键字 var let 可以声明在同一行
  • 缩进 getter setter willSet didSet
  • 变量前不需要添加 internal关键字,因为这是默认的。同样不需要重复写重写方法内的代码

<a id="use-of-self"></a>

Use of Self(self 用法)

避免使用self 去调用属性或方法。

仅在初始化方法的参数名和属性名相同时用self

class BoardLocation {
  let row: Int, column: Int

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

<a id="computed-properties"></a>

Computed Properties(计算属性)

如果一个计算属性的返回值很简单,可以省去 get{}。有 set{} 就一定要有 get{}

Preferred(推荐):

var diameter: Double {
  return radius * 2
}

Not Preferred(不推荐):

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

<a id="final"></a>

Final(关键字)

当你不希望该类被继承时,用 final

// 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
  }
}

<a id="function-declarations"></a>

Function Declarations(函数声明)

保持短方法名在一行

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

如果方法名换行,下一行添加一个缩进距离

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

<a id="closure-expressions"></a>

Closure Expressions(闭包表达式)

闭包放在最后面

Preferred(推荐):

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(不推荐):

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

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

对单行的闭包,返回值可以用含蓄方式

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

链式方法后面跟随闭包,方法名应该清晰明了。

let value = numbers.map { $0 * 2 }.filter { $0 % 3 == 0 }.indexOf(90)

let value = numbers
   .map {$0 * 2}
   .filter {$0 > 50}
   .map {$0 + 10}

<a id="types"></a>

Types(类型)

如无必要,用 swift 类型,不要用 oc 类型

Preferred(推荐):

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

Not Preferred(不推荐):

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

在Sprite Kit编程中,用CGFloat以便代码更简明,避免各种转换。

<a id="constants"></a>

Constants

常量用 let,变量用var

Tip: A good technique is to define everything using let and only change it to var if the compiler complains!

你可以将常量定义到一个类型中而不是作为实例的类型属性。 类型属性用static let

Preferred(推荐):

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

radius * Math.pi * 2 // circumference

Note:

The advantage of using a case-less enumeration is that it can't accidentally be instantiated and works as a pure namespace.

Not Preferred(不推荐):

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

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

<a id="static-methods-and-variable-type-properties"></a>

Static Methods and Variable Type Properties

static方法和类型的功能类似全局函数和全局变量

<a id="optionals"></a>

Optionals(可选值)

声明一个函数的某个参数可以为 nil 时,用?

当你确定某个变量在使用时已经确定不是 nil 时,在后面加!

......

self.textContainer?.textLabel?.setNeedsDisplay()

Use optional binding when it's more convenient to unwrap once and perform multiple operations:

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

命名一个 optional 变量,避免使用optionalStringmaybeView,因为他们已经在声明时体现出来了。

optional 绑定,使用原始名称而不是unwrappedViewactualLabel

Preferred(推荐):

var subview: UIView?
var volume: Double?

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

Not Preferred(不推荐):

var optionalSubview: UIView?
var volume: Double?

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

<a id="struct-initializers"></a>

Struct Initializers(结构体初始化)

Preferred(推荐):

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

Not Preferred(不推荐):

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

Prefer the struct-scope constants CGRect.infinite, CGRect.null, etc. over global constants CGRectInfinite, CGRectNull, etc. For existing variables, you can use the shorter .zero.

<a id="lazy-initialization"></a>

Lazy Initialization(懒加载)

Consider using lazy initialization for finer grain control over object lifetime. This is especially true for UIViewController that loads views lazily. You can either use a closure that is immediately called { }() or call a private factory method. Example:

用懒初始化

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] is not required here. A retain cycle is not created.
  • Location manager has a side-effect for popping up UI to ask the user for permission so fine grain control makes sense here.
  • 不需要使用[unowned self],因为没有创建循环引用
  • ......

<a id="type-inference"></a>

Type Inference(类型)

Prefer compact code and let the compiler infer the type for constants or variables of single instances. Type inference is also appropriate for small (non-empty) arrays and dictionaries. When required, specify the specific type such as CGFloat or Int16.

Preferred(推荐):

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

Not Preferred(不推荐):

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

<a id="type-annotation-for-empty-arrays-and-dictionaries"></a>

Type Annotation for Empty Arrays and Dictionaries

用以下方式创建 array dictionary

Preferred(推荐):

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

Not Preferred(不推荐):

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

NOTE: Following this guideline means picking descriptive names is even more important than before.

<a id="syntactic-sugar"></a>

Syntactic Sugar(语法窍门)

推荐用短语义声明

Preferred(推荐):

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

Not Preferred(不推荐):

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

<a id="functions-vs-methods"></a>

Functions vs Methods(函数 方法)

class 或类型以外的自由函数应该单独使用。如果可以,用方法而不是函数。这样会更容易获得和发现。

自由函数不应该跟任何类型或实例关联。

Preferred(推荐)

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

Not Preferred(不推荐)

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

Free Function Exceptions

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

<a id="memory-management"></a>

Memory Management(内存管理)

代码尽量避免循环引用,避免强引用,用weakunowned引用。用值类型避免循环引用。

<a id="extending-lifetime"></a>

Extending object lifetime(扩展生命周期)

[weak self]guard let strongSelf = self else { return } 模式扩展生命周期。用[weak self][unowned self]更好。

Preferred(推荐)

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

Not Preferred(不推荐)

// 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(不推荐)

// 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)
}

<a id="access-control"></a>

Access Control(访问控制)

Full access control annotation in tutorials can distract from the main topic and is not required. Using private appropriately, however, adds clarity and promotes encapsulation. Use private as the leading property specifier. The only things that should come before access control are the static specifier or attributes such as @IBAction and @IBOutlet.

......

Preferred(推荐):

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

Not Preferred(不推荐):

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

<a id="control-flow"></a>

Control Flow(控制流)

更推荐 for-in 而不是 while-condition-increment

Preferred(推荐):

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(不推荐):

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
}

<a id="golden-path"></a>

Golden Path(黄金路线)

多个条件判断时,不要多个if嵌套,用 guard

Preferred(推荐):

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(不推荐):

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
  }
}

判断多个 optional 时,用 guardif let。减少嵌套。

Preferred(推荐):

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

Not Preferred(不推荐):

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")
}

<a id="failing-guards"></a>

Failing Guards

Guard 要求以某种方式退出。一般可以用 return, throw, break, continue, and fatalError()。避免大量代码

<a id="semicolons"></a>

Semicolons(分号)

swift 不要求写;,仅仅在你把多个语句写在同一行时才要求;

不要把多个状态语句写在同一行

for-conditional-increment才会将多个语句写在同一行。但是 swift 推荐用for-in

Preferred(推荐):

let swift = "not a scripting language"

Not Preferred(不推荐):

let swift = "not a scripting language";

NOTE: generally considered unsafe

<a id="parentheses"></a>

Parentheses(圆括号)

圆括号也不是必须的

Preferred(推荐):

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

Not Preferred(不推荐):

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

<a id="smiley-face"></a>

Smiley Face(笑脸)

笑脸的正确表示方法

Preferred(推荐):

:]

Not Preferred(不推荐):

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

推荐阅读更多精彩内容

  • 从其他地方整理了一些编码规范的资料,分享给大家。YoY 1.语言(Language) 需要使用US English...
    大脸猫121阅读 431评论 0 2
  • **2014真题Directions:Read the following text. Choose the be...
    又是夜半惊坐起阅读 9,259评论 0 23
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,517评论 18 139
  • 过去已经过去,未来还未来。 生命最好的期待,这一刻展开。 当过去已经成为过去式,未来还没有到来。 生命最好的期待,...
    疯狂吧_e0ca阅读 2,074评论 1 2
  • 不知不觉已迈入三十岁了,以前总在幻想三十岁的样子,职场白领,有美丽的妻子,有个女儿,然而现实总让人大跌眼镜...
    canhe98阅读 307评论 1 2