剑客
关注科技互联网

iOS 10 by Tutorials 笔记(一)

Chapter 1: What’s New in Swift 3

Swift 3 相对 Swift 2 带来了巨大的改变,首先很多以 NS 开头的框架都有了对应的,更加 Swifty 的值类型,这样方便 Swift 在其他平台上进行推广。

对一些低级 C API 进行了封装,现在能像使用 Swift 原生类型一样去调用它们了。

最后是一些语言层级的变化,现在进入本章的内容

The Grand Renaming

首先是框架中方法名的变化,比较下面三个方法:

Objective-C

UITableViewCell *cell =
  [tableView cellForRowAtIndexPath: indexPath];

Swift 2

let cell = tableView.cellForRowAtIndexPath(indexPath)

Swift 3

let cell = tableView.cellForRowAt(indexPath)

我们关于方法命名可以总结三点:

  1. 方法命名尽量读起来像英语句子
  2. 变量名也作为句子的一部分
  3. 避免重复的词语

我们可以从 UIKit 和 Foundation 方法中看到这些变化

// Swift 2 definition
prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)  
// Swift 2 calling code
viewController.prepareForSegue(segue, sender: something)  
// Swift 3 definition
prepare(for segue: UIStoryboardSegue, sender: Any?)  
// Swift 3 calling code
viewController.prepare(for: segue, sender: something)

Swift 3 方法被调用时,第一个参数前 label 会默认出现

// Function definition:
func engageFluxCapacitor(fluxCapacitor: FluxCapacitor)  
// Called in Swift 2:
timeMachine.engageFluxCapacitor(fluxCapacitor)  
// Called in Swift 3:
timeMachine.engageFluxCapacitor(fluxCapacitor: fluxCapacitor)

Xcode 提供了迁移工具,方便我们自动处理

如果在第一个参数 label 前加 _
表示调用时可以省略『参数 label』,因为第一个参数名已经使得方法名含义清晰了

func engage(_ fluxCapacitor: FluxCapacitor)  
// Called like this:
timeMachine.engage(fluxCapacitor)

Overloading

如果两个方法,方法名都相同,只能依靠参数类型来区分,最好确定这两个方法做类似的事情,比如有两个 add(_:)
方法,分别添加一个或添加多个元素,那么这种定义是 OK 的,毕竟他们在语义上做了相同的事情

但如果做不同的事情,比如有一个 VideoLoader 类,它有一个 VideoRequest 对象用来请求数据,另一个 VideoOutputHandler 用来处理数据。这样再用 add(_:)
方法添加他们时就最好写成两个方法 addLoader(_:)
addOutput(_:)

Grammatical rules

不要在参数前加 label,除非它读起来不是一个完整的句子

// Doesn't read like a sentence
let word = wordList.word(1)  
// Reads like a sentence
let word = wordList.word(at: 1)  
// Function definition:
func word(at index: Int) -> String {  
... }

有副作用的方法,应该用动词作为方法名

// "Sort" is a verb, the sorting is in-place,
// so it is a side effect.
mutating func sortAlphabetically() {  
    ... 
}

在计算机科学中,函数副作用指当调用函数时,除了返回函数值之外,还对主调用函数产生附加的影响。例如修改全局变量(函数外的变量)或修改参数。 函数副作用会给程序设计带来不必要的麻烦,给程序带来十分难以查找的错误,并降低程序的可读性。 严格的函数式语言要求函数必须无副作用。

值类型通常有一个不可变和可变的方法版本,一般会返回一个新实例然后修改,根据情况命名方法时加上 ed/ing
关键词

// sortED
func sortedAlphabetically() -> WordList {  
    ... 
}
// Remove is a verb
mutating func removeWordsContaining(_ substring: String) {  
    ... 
}
// RemovING
func removingWordsContaining(_ substring: String) -> WordList {  
    ... 
}

命名 Bool 属性,最前面要加 is
关键字

var isSortedAlphabetically: Bool

Foundation value types

Swift 3 中更多的 Foundation 类型有了值语义,比如之前就有的 String,现在又新增了 Date 等类型(对应了之前的 NSDate)

虽然是值类型,但由于底层采用了写时复制的机制(copy on write),并不会占用太多内存

Working with C APIs

GCD 和 Core Graphics 是 iOS 开发者使用最多的 C APIs,而 Swift 3.0 对其进行了封装。

新的 GCD 语法

let queue = DispatchQueue(  
  label: "com.razeware.my-queue"
  qos: .userInitiated)

queue.async {  
  print("Hello from my serial queue!")
}

queue.async {  
  print("Hello from my serial queue!")
}

queue.async {  
  print("Hello from my serial queue!")
}

Core Graphics 也一样

let transform = CGAffineTransform.identity  
  .scaledBy(x: 0.5, y: 0.5)
  .translatedBy(x: 100, y: 100)
  .rotated(by: .pi / 4)

let rectangle = CGRect(x: 5, y: 5, width: 200, height: 200)  
context.setFillColor(UIColor.yellow.cgColor)  
context.setStrokeColor(UIColor.orange.cgColor)  
context.setLineWidth(10)  
context.addEllipse(in: rectangle)  
context.drawPath(using: .fillStroke)

Language feature changes

最后是语言层面上的一些变化

Increment operators

自增自减运算符 counter++ 彻底被移除了

C-Style for loops

传统 C 风格的循环由于抛弃了 ++ 运算符,变成了

for i in 0..<10 {
  print(i)
}

Currying syntax

柯理化语法被移除了

Key paths

我们在 OC 时代就大量使用 Key paths 和 key-value coding,不过它是工作在运行时,虽然很强大,但存在很多隐患。Swift 3.0 提供了更加安全的获取 key path 方式,并且他会在编译时验证。

class TimeMachine: NSObject {  
  var currentYear = 2016
}
let timeMachine = TimeMachine()  
timeMachine.value(forKey: #keyPath(TimeMachine.currentYear))  
// gives 2016

因为 key-value 只支持类,而且由 Objective-C runtime 来实现,所以如果要对原生 swift class 中的 属性
使用 #keyPath
语法,必须在声明这个属性时加上 dynamic 前缀。

class TimeMachine {  
  dynamic var currentYear = 2016
  var destinationYear = 1985
}

#keyPath(TimeMachine.currentYear) // "currentYear"
#keyPath(TimeMachine.destinationYear) // Error

从 NSObject 继承的类定义属性时,无需加这个 dynamic

Access control

Swift 3 的访问控制关键词变成了 5 个:

  • open 代码完全公开,可以在任何地方进行子类化。一般用在 UIKit 和 Foundation 类中,鼓励继承
  • public 代码对外完全可见,但在同一个 module 中才允许继承
  • internal 代码对同一个 module 中的可见
  • fileprivate 代码在同一个文件内可见
  • private 代码只针对同一个声明内可见

关于 private 一般针对将一个文件用 extension 分割使用

class PotatoListViewController: UIViewController {  
  private var potatoes: [Potato]
    ... 
}
extension PotatoListViewController: PotatoSelectionDelegate {  
  func didDelete(_ potato: Potato) {
    // potatoes 在此不可访问
    potatoes.remove(potato)
  }
}

Enums

枚举实例使用时都变成小写开头了

// Swift 2
label.textAlignment = .Center  
// Swift 3
label.textAlignment = .center

而在枚举中使用自身的时候也必需写 .xxx 而不能直接用 xxx 来表示了

enum Size {  
  case Big
  case Little
  case Tiny
  var isSmall: Bool {
    switch self {
    // 不能这样写了,要写成 case .Big: return false
      case Big: return false
      case .Little: return true
      case .Tiny: return false
} }
}

Closures

Swift 3.0 中的闭包作为参数传入时,默认变成了非逃逸闭包(以前是逃逸闭包),如果需要逃逸闭包,就要手动声明为 @escaping

func doSomethingWith(_ this: Thing, then: @escaping (Thing) -> ()) {  
  self.completion = then
  ... do stuff in the background ...
}

而且在非逃逸闭包内部就不必使用 self 来明确作用域了

func doSomething(_ then: () -> ()) {  
    // do something
    then() 
}
// Swift 2
doSomething {  
    self.finished = true
}
// Swift 3
doSomething {  
    finished = true
}

默认 non-escaping 有一个很重要的规则: 它只适用于作为参数传入函数的闭包。例如: 任何作为参数传入的闭包. 其它所有闭包都是 escaping 的

更多闭包细节见这篇 文章

-EOF-

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址