WWDC2015 -- What's New in Swift (Session 106)

标签(空格分隔): WWDC


Swift2的目标

  1. 希望Swift语言核心的特性和行为以及它的工具变好。
  2. 编写优秀的和安全的代码
  3. 富有表现力的库与API

五个在Swift中新特性

  1. Fundamentals
  2. Pattern Matching
  3. Availability Checking
  4. Protocal Extensions
  5. Error Handling

Fundamentals

enum  Animals{
case Dog,Car,Dragon
}
let a = Animals.Dragon
print(a)

枚举携带更多信息,而且可以打印出来。


enum Either{
case First(T1)
case Second(T2)
}

可以用相关值关联方式创建数组。


enum Tree {
  case Leaf(T)
indirect case Node(Tree, Tree) 
}

递归支持。


do {
  ...
  ...
}while()
repeat {
  let a = Animals.Troll
  ...
}while()

用repeat-while代替了do-while,从字面上意义更加明确。也避免与异常处理混淆。


Swift 1:

   viewAnimationOptions = .Repeat | .CurveEaseIn | .TransitionCurlUp
   viewAnimationOptions = nil
   if viewAnimationOptions & .TransitionCurlUp != nil {

Swift2:

   viewAnimationOptions = [.Repeat, .CurveEaseIn, .TransitionCurlUp]
   viewAnimationOptions = []
   if viewAnimationOptions.contains(.TransitionCurlUp) {

对于选项设置,用集合代替了位操作符。变得简单又强大。


struct MyFontStyle : OptionSetType {
    let rawValue : Int
    static let Bold = MyFontStyle(rawValue: 1)
    static let Italic = MyFontStyle(rawValue: 2)
    static let Underline  = MyFontStyle(rawValue: 4)
    static let Strikethrough = MyFontStyle(rawValue: 8)
}
var myFont :MyFontStyle = MyFontStyle()
myFont = []
myFont = [.Underline]
myFont = [.Bold, .Italic]
if myFont.contains(.Strikethrough) {
}

注意这里的rawValue为数值相加。


func save(name: String, encrypt: Bool) { ... }

class Widget {
func save(name: String, encrypt: Bool) { ... } 
···
}
save("thing", encrypt: false)
widget.save("thing", encrypt: false)

将方法与函数统一为单一的函数声明。
统一了函数参数标签。


func save(name: String, encrypt: Bool) {  }
save("123", encrypt: false)
func save(name name: String, encrypt: Bool) {  }
save(name: "123", encrypt: true)
func save(name: String, _ encrypt: Bool) {  }
save("123", true)

命名方式对使用方式的影响。


struct MyCoordinates {
    var points : [CGPoint]
    func updatePoint() {
        points[42].x = 19
    }
}

编译器更新,能够对以前不能识别的错误进行诊断。



var abc= ···

let size = abc 

var indices = abc
indices.sort()

对三种情况进行提示。

  • 不会被修改的变量使用了var提示使用let。
  • 从来没有被使用的变量。
  • 方法的返回值没有被使用。

//SDK Improvements

class func requestHeaderFieldsWithCookies(cookies: [AnyObject]!)
     -> [NSObject : AnyObject]!
     
//现在为
class func requestHeaderFieldsWithCookies(cookies: [AnyObject] )
     -> [NSObject : AnyObject]
     
//使用时
class func requestHeaderFieldsWithCookies(cookies: [NSHTTPCookie]) 
-> [String: String] 

采纳了新的特性与更好的规则:

  • Nullability修饰。
  • Objective-C 类型集合。
  • NS_ENUM, NS_OPTIONS, instancetype, @property, etc。

 @testable
 import MyApp

Swift1中如果需要测试,需要将内容设置为public,但其实很多内容不应该被public。现在在测试中这么写,就能解决问题。


/**:
 
 ##可以使用MarkDown方式进行注释
 
 ####真的可以
 
 - 1
 - 2
 - 3
  
 >print("Hello World")
  
 */
class Hello {
}

可以用MarkDown的方式进行注释。这是一个伟大的,著名的,非常受欢迎,可爱的语法。


可以将Swift1自动转化为Swift2


Pattern Matching

func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if let dest = segue.destinationViewController as? BlogViewController
...
    } 
}

if-let 语句使用。

func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if let dest = segue.destinationViewController as? BlogViewController{
if let blogIndex = tableView.indexPathForSelectedRow()?.row { if segue.identifier == blogSegueIdentifier {
                dest.blogName = swiftBlogs[blogIndex]
                ...
                ...
             }
        }
    }
}

如果嵌套太多会对结构产生如同金字塔一样的灾难。

func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if let dest = segue.destinationViewController as? BlogViewController
if let blogIndex = tableView.indexPathForSelectedRow()?.row { if segue.identifier == blogSegueIdentifier {
                dest.blogName = swiftBlogs[blogIndex]
                ...
                ...
            } 
        }
    }
}

在swift1.2中引入复合条件语句,这使得代码显得比较自然。
可以检查多个自选和布尔条件合适的嵌入。虽然比之前好但是这并没有解决早期存在的问题。


func process(json: AnyObject) -> Either {
    guard let name = json["name"] as? String else {
        return .Second("missing name")
    }
     guard let year = json["year"] as? Int else{
        return .Second("missing year")
    }
    let person = processPerson(name, year)
    return .First(person)
}
//可以合并书写
func process(json: AnyObject) -> Either {
    guard let name = json["name"] as? String,
          let year = json["year"] as? Int else {
        return .Second(“bad input”)
    }
    let person = processPerson(name, year)
    return .First(person)
}

引入了新的Guard语句,使用Guard-else,将许多工作在Guard语句中声明,同时检查多个语句,更加简洁,也是更好提前结束的方式。


enum Animals{
    
    
    case Dog(Int) ,Car(String),Dragon(Float)
}

func myDog()->Animals
{
    return Animals.Dog(5)
}
switch Dog() {
case .Dog(let value)  where value != 42:
    print(value)
······
default: break
    
}
//可以只对一个条件判断
if case .Dog(let value) = myDog() where value != 42 {
    print(value)
}

使用switch的时候我们需要详细的描述各种条件,但是如果只需要等于一种case进行判断,可以缩写。


//以前的方式
for value in mySequence {
    if value != "" {
        doThing(value)
    }
}
//新的方式1
for value in mySequence where value != "" {
    doThing(value)
}
//新的方式2
for case .MyEnumCase(let value) in enumValues {
    doThing(value)
}

为了快速的循环做了两件事。

  1. 加入内联能力。
  2. 更强大的条件匹配。

API Availability Checking

  • 在不抛弃用户的情况下利用新的能力
  • 简单而且丰富的检查
  • 通过语言静态的实现
@IBOutlet var dropButton: NSButton!
override func awakeFromNib() {
  dropButton.springLoaded = true
}

springLoaded这个API是在MAC OS 10.10.3中后出现,如果程序中这么写,如果使用的系统环境低于10.10.3就会出错。

@IBOutlet var dropButton: NSButton!
override func awakeFromNib() {
  if dropButton.respondsToSelector("setSpringLoaded") {
      dropButton.springLoaded = true
  }
}

用respondsToSelector去检查方法是一种很常见的解决方式,但是这种方式并不够好,是棘手的、容易出错的模式。比如我忘记方法的名称或者拼写错误。

error: springLoaded is only available on Mac OS X 10.10.3 or later

在Swift2中,编译器会更具你的minimum deployment target去判断是否能够使用所有的API。上面就是判断的结果。

@IBOutlet var dropButton: NSButton!
override func awakeFromNib() {
    if #available(OSX 10.10.3, *) {
        dropButton.springLoaded = true
    }
}

加入了新的#available判断条件,可以直接根据运行环境判断是否使用。


Protocal Extensions

  • 在标准库中更多持久化的方法使用
  • 新的有效的设计模式
extension Array{
func countIf(match: Element ->Bool) -> Int {
        var n = 0
        for value in self {
            if match(value) { n++ }
        }
        return n
    }
}

扩展是一个非常强大的功能,迅速。可以采取任意类型的数组,并添加我自己的方法。当我添加一个扩展,我真的增加了新的功能,感觉只是作为第一类,只是作为核心的类型的任何类型的设计师可能已经添加了。


func countIf(match: ){collection:T,match: T.Generator.Element->Bool) -> Int {
    var n = 0
    for value in self {
        if match(value) { n++ }
    }
return n }

该方法是一个全局函数。

  1. 这是一个额外的语法,可能会被忽略使用。
  2. 在Array方法实现时并不会显示在数组中的任何功能列表中。
extension CollectionType {
func countIf(match: Element -> Bool) -> Int {
        var n = 0
        for value in self {
            if match(value) { n++ }
        }
return n }
}

在Swift2中,我们可以直接对协议进行扩展,这样所有支持协议的类都能使用该方法。


let numbers = [1,2,3,4]

//swift1
let x = filter(map(numbers) { $0 * 3 }) { $0 >= 0 }

//swift2
let x = numbers.map { $0 * 3 }.filter { $0 >= 0 }

全局的泛型方法变为函数。

Error Handling

在Swift上很很大提升空间的地方

  • 安全性
  • 可读性
  • 编写性

共同的问题

  • 太容易忽视错误
  • 重复的,有错误倾向的模式
  • 很难去思考隐晦的行为

错误的种类

  • 琐碎的小错误
  • 逻辑错误
  • 可以被知道细节可以被发现的错误
func preflight() {
    url.checkResourceIsReachable()
    resetState()
}

这个方法中url.checkResourceIsReachable()可能会失败。如果使用NSError呢会怎样?

func preflight(inout error: NSError?) -> Bool {
    if url.checkResourceIsReachableAndReturnError(&error) {
        return false
    }
    resetState()
    return true
}

原本只有两行简单的代码现在拓展了很多内容,有了if语句的嵌套,并且有许多格外的参数。
在没有编译器帮助的情况下,手动操作加上返回条件。
只能在返回false的时候会发生错误,而我只是想知道错误的方式。

显然checkResourceIsReachableAndReturnError能出错。
显然preflight方法能够出错。
所有的控制流都清晰。(并不需要知道所有的情况)

func preflight() throws {
    try url.checkResourceIsReachable()
    resetState()
}

  1. 在可能出错的语句前加上try。
  2. 抛出异常,但没有处理异常。
    如果需要处理异常呢?
func preflight() -> Bool {
    do {
        try url.checkResourceIsReachable()
        resetState()
        return true
    } catch let error {
        return false
    } 
}

使用do-catch语句处理,将错误储存为error。


func preflight() -> Bool {
    do {
        try url.checkResourceIsReachable()
        resetState()
        return true
    } catch NSURLError.FileDoesNotExist {
        return true
    } catch let error {
        return false
    } 
}

可以对于指定的错误进行处理。


func preflight() {
    try! url.checkResourceIsReachable()
    resetState()
}

如果遇到很严重的致命错误,抛出后程序会崩溃,可以使用try!,这样虽然进行了处理实际上没有抛出错误。


ErrorType is a protocol in the Swift standard library
我们能够用枚举创建自己的错误类型准守协议

enum DataError : ErrorType {
    case MissingName
    case MissingYear
    // add more later
}
func processPerson(json: AnyObject) -> Either {
  guard let name = json["name"] as? String {
    return .Second("missing name")
  }
  guard let year = json["year"] as? Int {
    return .Second("missing year")
}
  return .First(Person(name, year))
}

可以改为

func processPerson(json: AnyObject) -> Either {
  guard let name = json["name"] as? String {
    throw DataError.MissingName
  }
  guard let year = json["year"] as? Int {
    throw DataError.MissingYear
}
  return .First(Person(name, year))
}

并且修改返回值,因为不需要再使用Either了。

func processPerson(json: AnyObject) throws -> Person {
  guard let name = json["name"] as? String {
    throw DataError.MissingName
  }
  guard let year = json["year"] as? Int {
    throw DataError.MissingYear
}
  return Person(name, year)
}


func processSale(json: AnyObject) throws {
  delegate?.didBeginReadingSale()
  guard let buyerJSON = json["buyer"] as? NSDictionary {
    throw DataError.MissingBuyer
  }
  let buyer = try processPerson(buyerJSON)
  guard let price = json["price"] as? Int {
    throw DataError.MissingPrice
  }
  delegate?.didEndReadingSale()
  return Sale(buyer, price)
}

我在这个方法中我需要加上个代理,在开始与结束的时候处理。
但是如果出现错误以及异常,会在delegate?.didEndReadingSale()执行之前就需要跳出。

func processSale(json: AnyObject) throws {
  delegate?.didBeginReadingSale()
  guard let buyerJSON = json["buyer"] as? NSDictionary {
    delegate?.didEndReadingSale()
    throw DataError.MissingBuyer
  }
  let buyer : Person
  do {
buyer = try processPerson(buyerJSON) } catch { 
    delegate?.didEndReadingSale()
throw error }
  guard let price = json["price"] as? Int {
    delegate?.didEndReadingSale()
 throw DataError.MissingPrice
   return Sale(buyer, price)
}

需要在每一个跳出方法的地方添加上delegate。
繁琐而且容易出错。

func processSale(json: AnyObject) throws {
  delegate?.didBeginReadingSale()
  defer { delegate?.didEndReadingSale() }
  guard let buyerJSON = json["buyer"] as? NSDictionary {
    throw DataError.MissingBuyer
  }
  let buyer = try processPerson(buyerJSON)
  guard let price = json["price"] as? Int {
    throw DataError.MissingPrice
  }
  return Sale(buyer, price)
}

这时候我们只需要将需要执行的代码放入defer中,只要在方法结束时就会自动触发defer执行。


『Exception handling』schemes

  • 任何都能被抛出。
  • 性能与异常非常相关,并且成反比。

Swift的错误处理更加平衡

  • Not table-based unwinding。
  • 与if判断语句的性能相近。

Swift用Cocoa API的错误处理设计非常优雅。
能够自动识别在Cocoa中常见的约定。

//Swift1
extension NSIncrementalStore {
func loadMetadata(error: NSErrorPointer) -> Bool
}
//Swift 2:
 extension NSIncrementalStore {
   func loadMetadata() throws
}
//Swift 1:
extension NSData {
class func bookmarkDataWithContentsOfURL(bookmarkFileURL: NSURL, 
-> NSData? }
//Swift 2:
extension NSData {
class func bookmarkDataWithContentsOfURL(bookmarkFileURL: NSURL) throws
-> NSData }

例如,方法有一个NSError参数并返回bool类型。
自动添加throw方法并取消bool返回值。
同样,如果它返回一个可选的结果,我们认识到,返回值为零表示无效的东西,它不再返回一个可选的结果,归入错误处理。
正是在这2个非常简单的规则,我们发现绝大多数的在系统中的API与新的Swift错误处理模型结合变得【无缝】而且【美观】,我们认为在Swift中这是一个伟大的新的方式来处理错误。

你可能感兴趣的:(WWDC2015 -- What's New in Swift (Session 106))