黄金准则

一般来说,Apple 都是正确的,遵循 Apple 喜欢的或者示范的处理方式。在任何情况下,你都应该遵循 Apple 的代码风格,正如他们The Swift Programming Language 这本书里面的定义一样。然而 Apple 是个大公司,我们将会看到很多在示例代码中的差异。

永远不要仅仅为了减少代码量而去写代码。尽量依赖Xcode中的自动补全代码,自动建议,复制和粘贴。详尽的代码描述风格对其他代码维护者来说是非常有好处的。即便如此,过度的冗余也会失去 Swift 的重要特性:类型推断。

最佳实践

1.命名

正如 Swift Programming Language 中的类型名称都是以大驼峰命名法命名的(例如:VehicleController)。

变量和常量则以小驼峰命名法命名(例如:vehicleName)。

你应该使用 Swift 模板去命名你的代码而不是使用 Objective-C 类前缀的风格(除非和 Objective-C 接连)。

不要使用任何匈牙利标识法( Hungarian notation )命名(例如:k为常量,m为方法),应使用简短的命名并且使用 Xcode 的类型 Quick Help (01.png+ click) 去查明变量的类型。同样地,不要使用小写字母+下划线( SNAKE_CASE )的命名方式。

唯一比较特别的是 enum 值的命名,这里需要使用大驼峰命名法(这个也是遵循 Apple 的 Swift Programming Language 风格):

enum Planet { case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune }

在所有可能的情况里,名称的不必要减少和缩写都应该避免,将来你应该能在没有任何损害和依赖 Xcode 的自动补全功能的情况下,确切地指出类型特征" ViewController "。非常普遍的缩写如 URL 是允许的。缩写应该用所有字母大写( URL )或者所有字母小写( url )表示。对类型和变量使用相同的规则。如果 url 是个类型,则应该为大写,如果是个变量,则应该为小写。

2.注释

注释不应该用来使代码无效,注释代码会使代码无效且影响代码的整洁。如果你想要移除代码,但是仍想保留以防代码在以后会用到,你应该依赖 git 或者 bug tracker 。

3.类型推断

在可能的地方,使用Swift的类型推断以减少多余的类型信息。例如,正确的写法:

var currentLocation = Location()

而不是:

var currentLocation: Location = Location()

4.Self 推断

让编译器在所有允许的地方推断 self 。在 init 中设置参数以及 non-escaping closures 中应该显性地使用 self 。例如:

struct Example { let name: String init(name: String) { self.name = name } }

5.参数列表类型推断

在一个闭包表达式( closure expression )中指定参数类型可能导致代码更加冗长。只有当需要指定类型时。

let people = [ ("Mary", 42), ("Susan", 27), ("Charlie", 18), ] let strings = people.map() { (name: String, age: Int) -> String in return "\(name) is \(age) years old" }

如果编译器能够推断类型,则应该去掉类型定义。

let strings = people.map() { (name, age) in return "\(name) is \(age) years old" }

使用排序好的参数编号命名("$0","$1","$2")能更好地减少冗余,这经常能够完整匹配参数列表。只有当closure的参数名称中没有过多的信息时,使用编号命名。(例如特别简单的 maps 和 filters )。

Apple 能够并将会改变由 Objective-C frameworks 转换过来的 Swift 的参数类型。例如,选项被移除或者变为自动展开等。我们应有意地指定你的选项并依赖 Swift 去推断类型,减少在这种情况下程序中断的风险。

你总是应该有节制地指定返回类型。例如,这个参数列表明显过分冗余:

dispatch_async(queue) { () -> Void in print("Fired.") }

6.常量

在类型定义的时候,常量应该在类型里声明为 static 。例如:

struct PhysicsModel { static var speedOfLightInAVacuum = 299_792_458 } class Spaceship { static let topSpeed = PhysicsModel.speedOfLightInAVacuum var speed: Double func fullSpeedAhead() { speed = Spaceship.topSpeed } }

使用 static 修饰常量可以允许他们在被引用的时候不需要实例化类型。

除了单例以外,应尽量避免生成全局常量。

7.计算型类型属性(Computed Properties)

当你只需要继承 getter 方法时,返回简单的 Computed 属性即可。例如,应该这样做:

class Example { var age: UInt32 { return arc4random() } }

而不是:

class Example { var age: UInt32 { get { return arc4random() } } }

如果你在属性中添加了 set 或者 didSet ,那么你应该显示地提供 get 方法。

class Person { var age: Int { get { return Int(arc4random()) } set { print("That's not your age.") } } }

8.实例转换(Converting Instances)

当创建代码去从一个类型转换到另外的 init() 方法:

extension NSColor { convenience init(_ mood: Mood) { super.init(color: NSColor.blueColor) } }

在 Swift 标准库中,对于把一个类型的实例转换为另外一种,现在看来 init 方法是比较喜欢用的一种方式。

"to" 方法是另外一种比较合理的技术(尽管你应该遵循 Apple 的引导去使用 init 方法):

struct Mood { func toColor() -> NSColor { return NSColor.blueColor() } }

而你可能试图去使用一个getter,例如:

struct Mood { var color: NSColor { return NSColor.blueColor() } }

getters 通常由于应该返回可接受类型的组件而受到限制。例如,返回了 Circle 的实例是非常适合使用 getter 的,但是转换一个 Circle 为 CGPath 最好在 CGPath 上使用"to"函数或者 init() 扩展。

9.单例(Singletons)

在Swift中单例是很简单的:

class ControversyManager { static let sharedInstance = ControversyManager() }

Swift 的 runtime 会保证单例的创建并且采用线程安全的方式访问。

单例通常只需要访问"sharedInstance"的静态属性,除非你有不得已的原因去重命名它。注意,不要用静态函数或者全局函数去访问你的单例。

(因为在 Swift 中单例太简单了,并且持续的命名已经耗费了你太多的时间,你应该有更多的时间去抱怨为什么单例是一个反模式的设计,但是避免花费太多时间,你的同伴会感谢你的。)

10.使用扩展来组织代码

扩展应该被用于组织代码。

一个实例的次要方法和属性应该移动到扩展中。注意,现在并不是所有的属性类型都支持移动到扩展中,为了做到最好,你应该在这个限制中使用扩展。

你应该使用扩展去帮助组织你的实例定义。一个比较好的例子是,一个 view controller 继承了 table view data source 和 delegate protocols 。为了使table view中的代码最小化,把 data source 和 delegate 方法整合到扩展中以适应相应的 protocol 。

在一个单一的源文件中,在你觉得能够最好地组织代码的时候,把一些定义加入到扩展中。不要担心把 main class 的方法或者 struct 中指向方法和属性定义的方法加入扩展。只要所有文件都包涵在一个 Swift 文件中,那就是没问题的。

反之,main 的实例定义不应该指向定义在超出 main Swift 文件范围的扩展的元素。

11.链式 Setters

对于简单的 setters 属性,不要使用链式 setters 方法当做便利的替代方法。

正确的做法:

instance.foo = 42 instance.bar = "xyzzy"

错误的做法:

instance.setFoo(42).setBar("xyzzy")

相较于链式setters,传统的setters更为简单和不需要过多的公式化。

12.错误处理

Swift 2.0 的 do/try/catch 机制非常棒。

13.避免使用try!

一般来说,使用如下写法:

do { try somethingThatMightThrow() } catch { fatalError("Something bad happened.") }

而不是:

try! somethingThatMightThrow()

即使这种形式特别冗长,但是它提供了context让其他开发者可以检查这个代码。

在更详尽的错误处理策略出来之前,如果把 try! 当做一个临时的错误处理是没问题的。但是建议你最好周期性地检查你代码,找出其中任何有可能逃出你代码检查的非法try!。

14.避免使用try?

try?是用来“压制”错误,而且只有当你确信对错误的生成不关心时,try?才是有用的。一般来说,你应该捕获错误并至少打印出错误。

15.过早返回&Guards

可能的话,使用guard声明去处理过早的返回或者其他退出的情况(例如,Fatal errors 或者 thorwn errors)。

正确的写法:

guard let safeValue = criticalValue else { fatalError("criticalValue cannot be nil here") } someNecessaryOperation(safeValue)

错误的写法:

if let safeValue = criticalValue { someNecessaryOperation(safeValue) } else { fatalError("criticalValue cannot be nil here") }

或者:

if criticalValue == nil { fatalError("criticalValue cannot be nil here") } someNecessaryOperation(criticalValue!)

这个flatten code以其他方式进入一个if let 代码块,并且在靠近相关的环境中过早地退出了,而不是进入else代码块。

甚至当你没有捕获一个值(guard let),这个模式在编译期间也会强制过早退出。在第二个if的例子里,尽管代码flattend得像guard一样,但是一个毁灭性的错误或者其他返回一些无法退出的进程(或者基于确切实例的非法态)将会导致crash。一个过早的退出发生时,guard声明将会及时发现错误,并将其从else block中移除。

16."Early"访问控制

即使你的代码没有分离成独立的模块,你也应该经常考虑访问控制。把一个定义标记为 private 或者 internal 对于代码来说相当于一个轻量级的文档。每一个阅读代码的人都会知道这个元素是不能“触碰”的。反之,把一个定义为 public 就相当于邀请其他代码去访问这个元素。我们最好显示地指明而不是依赖 Swift 的默认访问控制等级。( internal )

如果你的代码库在将来不断扩张,它可能会被分解成子模块.这样做,会使一个已经装饰着访问控制信息的代码库更加方便、快捷。

17.限制性的访问控制

一般来来说,当添加访问控制到你的代码时,最好有详尽的限制。这里,使用 private 比 internal 更有意义,而使用 internal 显然比 public 更好。(注意: internal 是默认的)。

如有需要,把代码的访问控制变得更加开放是非常容易的(沿着这样的途径: "private" to "internal" to "public") 。过于开放的访问控制代码被其他代码使用可能不是很合适。有足够限制的代码能够发现不合适和错误的使用,并且能提供更好的接口。一个例子就是一个类型公开地暴露了一个internal cache。

而且,代码的限制访问限制了“暴露的表面积”,并且允许代码在更小影响其他代码的情况下重构。其他的技术如:Protocol Driven Development 也能起到同样的作用。

作者:suiling
原文地址:http://www.cocoachina.com/swift/20151010/13664.html

Swift 最佳实践规范的更多相关文章

  1. ios – Swift中的UIView动画不起作用,错误的参数错误

    我正在尝试制作动画并使用下面的代码.我得到“无法使用类型’的参数列表调用’animateWithDuration'(FloatLiteralConvertible,延迟:FloatLiteralConvertible,选项:UIViewAnimationoptions,动画:()–>()–>$T4,完成:(Bool)–>(Bool)–>$T5)’“错误.这意味着我使用了错误的参数.我错了.请

  2. ios – 使用捕获列表中的无主内容导致崩溃,即使块本身也不会执行

    欣赏有关如何调试此内容的任何提示或有关导致崩溃的原因的解释……

  3. ios – Swift传递封闭与Params

    目前我传递一个闭包作为一个对象的属性,该对象不接受参数并且没有返回值,如下所示:到目前为止,这工作得很好.我希望能够在设置此闭包时传入一个参数,以便在MyClass的实例中使用.我正在寻找下面的SOMETHING,虽然我确定语法不正确:我如何将参数传递给可以在MyClass中使用的闭包–即可以在属性本身的didSet部分内使用的值,如第二个示例中所示?

  4. ios – 来自UIAlertController的self.navigationController?.popViewControllerAnimated

    我是新手,但我想我已经掌握了它.这让我的进步很难过.我想要做的是当我们无法找到他的查询的相关数据时向用户抛出错误消息,然后继续将他带回到之前的ViewController.但是,我在这方面遇到了麻烦.在我添加操作的行上,我收到以下错误:’UIViewController?’不是Void的子类型我该怎么做呢?

  5. ios – 如何添加@noescape注释到可选闭包

    我的功能有这个签名:而现在我想让自己在给定的关闭中逃脱自我.但是当我尝试这个:编译器抱怨:是否可以在可选参数中使用它?

  6. ios – Swift闭包为AnyObject

    如何将()–>()转换为AnyObject?我试图将它转换为:处理程序为AnyObject,但它给我一个错误说:()–>()不符合协议’AnyObject’解决方法HowcanIcast()->()intoAnyObject?

  7. ios – 在swift中将捕获列表正确放置在嵌套闭包中

    在Swift中为哪些嵌套闭包定义捕获的引用?如果[weakself]被捕获在只有内部最后面的闭包,GCD将保留ExampleDataSource,直到块完成执行,这就解释了为什么调试看起来像这样:同样的事情会发生,如果没有捕获列表被包括,我们从来没有可选地解开自己,尽管编译器,确实试图警告你!

  8. ios – 两个类,回调和单元测试

    而且,我不觉得这样的行为会很容易测试?

  9. ios – 为什么在Swift中使用私有属性/方法并不常见

    为什么在Swift/Cocoa中你很少看到私有访问器用于你不希望程序的其他部分访问的属性?有人可以如此善良并解释一般的概念访问器吗?

  10. 寒城攻略:Listo 教你 25 天学会 Swift 语言 - 18 Automatic Reference Counting

    //每个Person类的实例拥有一个被初始化为nil的apartment可选属性,因为一个人并不一定拥有一座公寓)}}classApartment{letnumber:Intinit{number=number}tenant:Person1?//声明Apartment2类中的tenant属性为弱引用varPin:Person2?鉴于这种关系,Customer类有一个可选类型属性card,而CreditCard类的customer属性则是非可选类型的classCustomer{varcard:CreditC

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部