标签(空格分隔): WWDC
Swift2的目标
- 希望Swift语言核心的特性和行为以及它的工具变好。
- 编写优秀的和安全的代码
- 富有表现力的库与API
五个在Swift中新特性
- Fundamentals
- Pattern Matching
- Availability Checking
- Protocal Extensions
- Error Handling
Fundamentals
enum Animals{
case Dog,Car,Dragon
}
let a = Animals.Dragon
print(a)
枚举携带更多信息,而且可以打印出来。
enum Either<T1,T2>{
case First(T1)
case Second(T2)
}
可以用相关值关联方式创建数组。
enum Tree<T> {
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<Person,String> {
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<Person,String> {
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)
}
为了快速的循环做了两件事。
- 加入内联能力。
- 更强大的条件匹配。
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: <CollectionType>){collection:T,match: T.Generator.Element->Bool) -> Int {
var n = 0
for value in self {
if match(value) { n++ }
}
return n }
该方法是一个全局函数。
- 这是一个额外的语法,可能会被忽略使用。
- 在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()
}
- 在可能出错的语句前加上try。
- 抛出异常,但没有处理异常。
如果需要处理异常呢?
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<Person,String> {
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<Person,String> {
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中这是一个伟大的新的方式来处理错误。