Swift/Cocoa 编码规范(中文)

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

简介

SlideShare 版

Swift

官方 SlideShare Swift 代码规范

这份规范是用于 swift 编写 iOS8 及以上应用。

目录


<a id="xcode-preferences"/>

Xcode Preferences

  • 用“空格键”代替“Tab键”,每个 Tab 对应4个空格(Xcode->Preferences->Text Editing->Indentation
    <a id=""/>

Switch

  • switch 状态应该有每个 case 状态,包括 default。
var value = 2
var test: String?

switch value {
case 1:
    test = "abc"
default:
    test = "xyz"
}
  • 如果你想要一个object 或 struct 的多个值,创建一个 tuple。如下:
struct TestValue {
    enum Val {
        case A
        case B
    }
    var value: Val = .A
    var detail: String = "Test"
}
var testValue = TestValue()

switch (testValue.value, testValue.detail) {
case (.A, "Test"):
    println("This is printed")
default:
    println("This is not printed")
}
  • 如果你不希望走到 default ,用 assert。
var test = "Hello"

switch test {
case "Hello"
    print("It prints")
case "World"
    print("It doesn't")
default:
    assert(false, "Useful message for developer")
}

<a id=""/>

Properties

  • 如果你希望创建一个只读计算属性,可以不需要 get{}
var computedProp: String {
    if someBool {
        return "Hello"
      }
}
  • 如果你希望创建一个可读写计算属性,必须实现 get{} set{}
var computedProp: String {
    get {
        if someBool {
            return "Hello"
          }
    }
    set {
        println(newValue)
    }
}
  • 同样的道理 willSet{} didSet{}
var property = 10 {
    willSet {
        println("willSet")
    }
    didSet {
        println("didSet")
    }
}
  • willSet 和 didSet 可以用 newValue 和 oldValue
var property = 10 {
    willSet {
        if newValue == 10 {
            println("It’s 10")
         }
    }
    didSet {
         if oldValue == 10 {
               println("It was 10")
         }
    }
}
  • 创建一个class常量用 static
class Test {
    static let ConstantValue: String = "TestString"
}

<a id=""/>

Closures

  • 闭包中不需要参数类型在参数列表里。另外,参数放在同一行。
doSomethingWithCompletion() { param1 in
    println("\(param1)")
}
  • 闭包放在方法参数的最后面。
// Definition
func newMethod(input: Int, onComplete methodToRun: (input: Int) -> Void) {
    // content
}

// Usage
newMethod(10) { param in
    println("output: \(param)"")
}
  • However, if there are 2 closures as the last parameters, do not use trailing closure syntax for the last one as this is ambiguous. Also, when creating a closure inline as a method parameter, put the parameter name on a new line and follow the following indentation rules:
  • 如果有两个闭包作为最后的参数......
testMethod(param: 2.5,
      success: {
        println("success")
      },
      failure: {
        println("failure")
      })
  • Use trailing closure syntax if a closure is the only parameter:
  • 如果闭包是仅有的参数......
array1.map { /* content */ }
  • If declaring the type of a function or closure with no return type, specify this by using Void as the return type. Also, put a space before and after -> when declaring a closure type:
  • 如果闭包返回类型为,......
func takeClosure(aClosure: () -> Void) {
    // content
}
  • 如果 func 或 closure 没有返回值,就不要写返回
func noReturn() {
    // content
}
  • 如果闭包太大,就不要在一行创建,重新创建一个local变量
func foo(something: () -> Void) {
    something()
}

func doEverything() {
    let doSomething = {
        var x = 1
        for 1...3 {
            x++
        }
        println(x)
    }
    foo(doSomething)
}

<a id=""/>

Identifiers

  • Only use self.<parameter name> if you need to, which is when you have a parameter of the same name as the instance variable, or in closures:
  • 仅仅在必要的时候用 self.
class Test {

    var a: (() -> Void)?
    var b: Int = 3

    func foo(a: () -> Void) {
        self.a = a
    }

    func foo1() {
        foo() {
            println(self.b)
        }
    }
}
  • 如果定义一个变量,冒号直接在变量后面,冒号后接空格
static var testVar: String
  • 定义字典类型,冒号前后各包含一个空格
var someDictionary: [String : Int]
  • 定义常量时,首字母大写,驼峰方式
class TestClass {
    let ConstantValue = 3
}
  • 用 struct 定义多个常量
struct Constants {
    static let A = "A"
    static let B = "B"
}

<a id=""/>

Singleton

  • 用以下方式实现单例
class ClassA {

    static let sharedInstance: ClassA = ClassA()
    
    private init() {
        // ...
    }
}

Note: Xcode currently (6.0.1) does not indent properly in this case. Use the indentation specified above.

<a id="optionals"/>

Optionals

  • When unwrapping optionals, rebind the optional to the same name, unless there is a reason not to. This example shows this, but in this case it should be done within the binding.
  • 使用 optional 变量时,使用相同的名字重新关联变量。
func possibleBike() -> Bike? {
    // content
}

let bike = possibleBike()
if let bike = bike {
    // content
}

<a id="strings"/>

Strings

  • When appending to a string, always use the += operator.
  • 用 += 拼接字符串
var newString = "Hello"
newString += " world!"

Note: do not concatenate user-facing strings as the ordering could change in different languages

<a id="enums"/>

Enums

  • When using an enum, always prefer the shorthand syntax when possible. The shorthand syntax should be possible whenever the type does not need to be inferred from the assigned value. Note: there are certain bugs that don't allow them to be used everywhere they should be possible.
  • 当使用enum,记住用短语义。
enum TestEnum {
    case A
    case B
}

var theValue: TestEnum?
var shouldBeA = true

if shouldBeA {
    theValue = .A
} else {
    theValue = .B
}
  • When declaring and setting a variable/constant of an enum type, do so in the following manner.
  • 声明一个enum变量或常量
var testValue: TestEnum = .A

<a id="documentation"/>

Documentation

  • 单行方法注释如下
/// This method does nothing
func foo() {
    // content
}
  • 多行注释如下
/**
 This method does something.
 It's very useful.
 */
func foo2() {
    // content
}
  • 用 swift 标准的注释方式以方便快速查看,用 Ctl+I 格式化代码

Note: Make sure to test your documentation by checking it's Quick Documentation by option-clicking on the method name.

/**
 This method has parameters and a return type.

 - Parameter input: This is an input parameter.
 - Returns: True if it worked; false otherwise.
 */
func foo3(input: String) -> Bool {
    // content
}

Note: The following section refers to marks, which are Swift's version of #pragma mark in Objective-C to separate code. There are 2 types of marks: section marks and sub-section marks.

Section Marks:

   // MARK: - Section Name

Sub-section Marks:

   // MARK: Sub-section Name
  • 用 marks 暗示新的代码段
class Stuff {

    // MARK: - Instance methods

    func newMethod() {
        // content
    }
}
  • class/struct 应该按照以下顺序组织

      - Section: Singleton
      - Section: Declared Types (Enums, Structs, etc.), Type Aliases
      - Section: Constants
      - Section: Class Properties
      - Section: Instance Properties
          - Sub-section: Stored
          - Sub-section: Computed
      - Section: Init/Deinit
      - Section: Class Methods
          - Sub-section: Public
          - Sub-section: Private
      - Section: Instance Methods
          - Sub-section: Public
          - Sub-section: Private
      - Section: Protocols
          - Sub-section: <Protocol Name>
    
  • 当一个 class 实现一个 protocol ,用扩展的形式,一个扩展对应一个 protocol

// NewProtocol.swift //
protocol NewProtocol {
    func reqMethod()
}

// Test.swift //
class Test {

    // content
}

// MARK: - Protocols
// MARK: NewProtocol

extension Test: NewProtocol {
    func reqMethod() {
        // content
    }
}

Cocoa

目录


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

Protocols

  • 重用 protocol 需要 reuse identifier.
protocol ReusableView {
    static var ReuseIdentifier: String { get }
    static var NibName: String { get }
}

<a id="uitableview"></a>

UITableView & UICollectionView

  • 在UITableViewCell/UICollectionViewCell的子类中,创建一个只读计算属性作为cell 的 reuse identifier,用大写开头,因为他是个常量。
class TableViewCell: UITableViewCell, ReusableView {
    static let ReuseIdentifier: String = "TableViewCellIdentifier"
    static let NibName: String = "CustomTableViewCell"
}

原因: 注册一个UITableView 或 UICollectionView的重用 cell , 你需要 nib 名称和重用 identifier.

<a id="strings"></a>

Strings

  • 国际化字符串放在变量中。
// Localizable.strings //
// <App Section>
"user_facing_string_key" = "This is a user-facing string."

// Someting.swift //
var userFacing = NSLocalizedString("user_facing_string_key", comment: "")

<a id="nsnotification"></a>

NSNotification

  • 用倒过来的域名加 notification name。
com.linkedin.slideshare.notification_name
  • 创建一个 struct 管理所有notification的常量名。
struct GlobalNotifications {
    static let ABC = ""
}
  • 创建 lazy notification 监听变量。
private lazy var handleNotification: (NSNotification!) -> Void { [weak self] notification in
    // Handle the notification
}

原因: This way you can define capture semantics for self and also use the identifier as the selector in the addObserver method (see below) instead of a string.确保编译安全.

  • 创建registerNotifications() 和 deregisterNotifications()。
func registerNotifications() {
    let notificationCenter = NSNotificationCenter.defaultCenter()

    notificationCenter.addObserver(self, selector: handleNotificationABC, name: GlobalNotifications.ABC, object: nil)
}

func deregisterNotifications() {
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

<a id="app-delegate"></a>

App Delegate

  • 将应用初始化代码抽象到一个单独的类中。
class AppInitializer {
    func onAppStart() {
        // content
    }
}

<a id="core-foundation"></a>

Core Foundation

  • 用如下方法代替 CGRectMake 等.
var rect = CGRect(x: 10, y: 10, width: 45, height: 300)
  • 创建初始化为0时应该使用如下方法:
var zeroRect = CGRect.zeroRect

<a id="view-controllers"></a>

View Controllers

  • 如果 controller 和一个 storyboard 关联,创建一个类方法名为createInstance返回一个用 storyboard 创建的实例。
static func createInstance() -> MasterViewController {
    return UIStoryboard.initialControllerFromStoryboard("Master") as! MasterViewController
}

Reasoning: 如果你不会创建当前类的子类,则用static ,否则用 class func

  • 如果你有以上描述的情形,但是需要初始化属性,可以创建designated/convenience initializer。
static func createInstanceWithId(id: Int) -> MasterViewController {
        let masterViewController = createInstance()
        masterViewController.id = id
        return masterViewController
}

<a id="uiview"></a>

UIView

  • 如果一个类继承自一个有 xib 初始化的 uiview,创建一个方法createInstance 用法如上文 View Controllers 部分。
class CustomView: UIView {

    private static let NibName: String = "CustomView"

    static func createInstance() -> CustomView {
        return NSBundle.mainBundle().loadNibNamed(nibName, owner: nil, options: nil)[0] as! CustomView
    }
}

<a id="objective-c-interoperability"></a>

Objective-C Interoperability

  • 你需要一个 bridging-header 文件。如果你的工程里面会用到 OC 文件。
// <Product-Name>-Bridging-Header.h
#import "SDWebImageHeader.h"

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

推荐阅读更多精彩内容

  • 以下翻译自Apple官方文档,结合自己的理解记录下来。翻译基于 swift 3.0.1 原文地址 Closure...
    艺术农阅读 1,524评论 0 3
  • 走着走着,就散了, 朋友越来越多,却也越来越少,越来越好。 回忆都淡了; 看着看着,就累了, 工作越来越多...
    炎燃阅读 433评论 0 2
  • 昨天,我路过国内某品牌腕表专柜,最近我注意到一些朋友开始关注这个品牌的腕表,便停下来和店员聊了聊,我试着拿他们的表...
    管理顾问王荣增阅读 420评论 0 1