转自: http://www.jianshu.com/p/f35514ae9c1a

WWDC 2017 带来了很多惊喜。Swift 4 也伴随着 Xcode 9 测试版来到了我们的面前,很多强大的新特性非常值得我们期待在正式项目中去使用它。因为 Swift 4 是开源的,如果你关注 swift-evolution 这个项目的话,就应该已经提前了解到它的新特性了。本文参考了 WWDC 2017 以及各种资料,从语法、字符串、标准库、构建过程等方面,把 Swift 4 的这些新特性一一列举出来做介绍和分析,让他们毫无保留地展现在你眼前。

一、语法改进

extension 中可以访问 private 的属性

考虑以下代码:

struct Date: Equatable, Comparable {
private let secondsSinceReferenceDate: Double
static func ==(lhs: Date, rhs: Date) -> Bool {
return lhs.secondsSinceReferenceDate == rhs.secondsSinceReferenceDate
}
static func <(lhs: Date, rhs: Date) -> Bool {
return lhs.secondsSinceReferenceDate < rhs.secondsSinceReferenceDate
}
}

上面代码定义了一个 Date 结构体,并实现 Equatable 和 Comparable 协议。为了让代码更清晰,可读性更好,一般会把对协议的实现放在单独的 extension 中,这也是一种非常符合 Swift 风格的写法,如下:

struct Date {
private let secondsSinceReferenceDate: Double
}
extension Date: Equatable {
static func ==(lhs: Date, rhs: Date) -> Bool {
return lhs.secondsSinceReferenceDate == rhs.secondsSinceReferenceDate
}
}
extension Date: Comparable {
static func <(lhs: Date, rhs: Date) -> Bool {
return lhs.secondsSinceReferenceDate < rhs.secondsSinceReferenceDate
}
}

但是在 Swift 3 中,编译就报错了,因为 extension 中无法获取到 secondsSinceReferenceDate 属性,因为它是 private 的。于是在 Swift 3 中,必须把 private 改为 fileprivate。

struct Date {
fileprivate let secondsSinceReferenceDate: Double
}
...

但是如果用 fileprivate,属性的作用域就会比我们需要的更大,可能会不小心造成属性的滥用。

在 Swift 4 中,private 的属性的作用域扩大到了 extension 中,并且被限定在了 struct 和 extension 内部,这样就不需要再改成 fileprivate 了,这是最好的结果。

类型和协议的组合类型

考虑以下代码:

protocol Shakeable {
func shake()
} extension UIButton: Shakeable { /* ... */ }
extension UISlider: Shakeable { /* ... */ } func shakeEm(controls: [???]) {
for control in controls where control.state.isEnabled {
}
control.shake()
}

在 Swift 3 中,这里的 ??? 应该写什么呢?如果写 UIControl,那么 control.shake() 就会报错;如果写 Shakeable,那么 control.state.isEnabled 就会报错。其实我们也可以这样写:

func shakeEm(controls: [UIControl]) {
for control in controls where control.isEnabled {
if control is Shakeable {
(control as! Shakeable).shake()
}
}
}

这样写虽然可以跑通了,但是很丑陋。

在 Swift 4 中,可以把类型和协议用 & 组合在一起作为一个类型使用,就可以像下面这样写了:

protocol Shakeable {
func shake()
} extension UIButton: Shakeable { /* ... */ }
extension UISlider: Shakeable { /* ... */ } func shakeEm(controls: [UIControl & Shakeable]) {
for control in controls where control.state.isEnabled {
control.shake()
}
}

把它声明为了 UIControl & Shakeable 类型。OK,圆满解决。

PS:
这个代码例子是 WWDC 2017 的 PPT 中的,上面的代码有点问题,control.state.isEnabled 这句代码中,state 是没有 isEnabled 这个属性的,改为 control.isEnabled 就可以了。看来苹果的工程师做 PPT 有时候还是不太严谨。

另外,iOS SDK 中的 API 也用这个特性做了优化,例如:

// Objective-C API
@interface NSCandidateListTouchBarItem<CandidateType> : NSTouchBarItem
@property (nullable, weak) NSView <NSTextInputClient> *client;
@end

这个 API 的 Objective-C 版本是没有问题的,可以知道 client 属性既是一个 NSView,又符合 NSTextInputClient 协议。然而它对应的 Swift 3 版本为:

class NSCandidateListTouchBarItem<CandidateType: AnyObject> : NSTouchBarItem {
var client: NSView?
}

仅仅是一个 NSView 类型 /(ㄒoㄒ)/~~

在 Swift 4 中,这类 API 做了优化,改成了:

class NSCandidateListTouchBarItem<CandidateType: AnyObject> : NSTouchBarItem {
var client: (NSView & NSTextInputClient)?
}

这样类型的声明就更加严谨了。

Associated Type 可以追加 Where 约束语句

在 Swift 4 中可以在 associatedtype 后面声明的类型后追加 where 语句

associatedtype Element where <xxx>

看下面是 Swift 4 标准库中 Sequence 中 Element 的声明:

protocol Sequence {
associatedtype Element where Self.Element == Self.Iterator.Element
// ...
}

它限定了 Sequence 中 Element 这个类型必须和 Iterator.Element 的类型一致。

通过 where 语句可以对类型添加更多的约束,使其更严谨,避免在使用这个类型时做多余的类型判断。

新的 Key Paths 语法

先来看看 Swift 3 中 Key Paths 的写法:

@objcMembers class Kid: NSObject {
dynamic var nickname: String = ""
dynamic var age: Double = 0.0
dynamic var friends: [Kid] = []
} var ben = Kid(nickname: "Benji", age: 5.5) let kidsNameKeyPath = #keyPath(Kid.nickname) let name = ben.valueForKeyPath(kidsNameKeyPath)
ben.setValue("Ben", forKeyPath: kidsNameKeyPath)

Swift 4 中创建一个 KeyPath 用 `` 作为开头:

\Kid.nickname

当编译器可以推导出类型时,可以省略基础类型部分:

\.nickname

上面的代码在 Swift 4 中就可以这样写:

struct Kid {
var nickname: String = ""
var age: Double = 0.0
var friends: [Kid] = []
} var ben = Kid(nickname: "Benji", age: 8, friends: []) let name = ben[keyPath: \Kid.nickname]
ben[keyPath: \Kid.nickname] = "BigBen"

相比 Swift 3,Swift 4 的 Key Paths 具有以下优势:

  1. 类型可以定义为 class、struct
  2. 定义类型时无需加上 @objcMembers、dynamic 等关键字
  3. 性能更好
  4. 类型安全和类型推断,例如 ben.valueForKeyPath(kidsNameKeyPath) 返回的类型是 Any,ben[keyPath: \Kid.nickname] 直接返回 String 类型
  5. 可以在所有值类型上使用

下标支持泛型

有时候会写一些数据容器,Swift 支持通过下标来读写容器中的数据,但是如果容器类中的数据类型定义为泛型,以前的下标语法就只能返回 Any,在取出值后需要用 as? 来转换类型。Swift 4 定义下标也可以使用泛型了。

struct GenericDictionary<Key: Hashable, Value> {
private var data: [Key: Value] init(data: [Key: Value]) {
self.data = data
} subscript<T>(key: Key) -> T? {
return data[key] as? T
}
} let dictionary = GenericDictionary(data: ["Name": "Xiaoming"]) let name: String? = dictionary["Name"] // 不需要再写 as? String

二、字符串

Unicode 字符串在计算 count 时的正确性改善

在 Unicode 中,有些字符是由几个其它字符组成的,比如 é 这个字符,它可以用 \u{E9} 来表示,也可以用 e 字符和上面一撇字符组合在一起表示 \u{65}\u{301}

考虑以下代码:

var family = "												

最全的 Swift 4 新特性解析的更多相关文章

  1. C++ 11学习和掌握 ——《深入理解C++ 11:C++11新特性解析和应用》读书笔记(一)

    因为偶然的机会,在图书馆看到<深入理解C++ 11:C++11新特性解析和应用>这本书,大致扫下,受益匪浅,就果断借出来,对于其中的部分内容进行详读并亲自编程测试相关代码,也就有了整理写出 ...

  2. Swift 3 新特性和迁移详解

    写在前面 Swift 3.0 正式版发布了差不多快一个月了,断断续续的把手上和 Swift 相关的迁移到了Swift 3.0.所以写点小总结. 背景 代码量(4万行) 首先,我是今年年初才开始入手 S ...

  3. php 7.1 新特性解析

    php 7.1 新特性解析 返回值和传入参数可以指定为 null <?php function testReturn(): ?string { return 'elePHPant'; } var ...

  4. Atitit.swift 的新特性 以及与java的对比 改进方向attilax 总结

    Atitit.swift 的新特性 以及与java的对比 改进方向attilax 总结 1. defer关键字1 2. try!形式存在的“不失败”机制3 3. Guard 4 4. swift的新语 ...

  5. c++学习书籍推荐《深入理解C++11 C++11新特性解析与应用》下载

    百度云及其他网盘下载地址:点我 编辑推荐 <深入理解C++11:C++11新特性解析与应用>编辑推荐:C++标准委员会成员和IBM XL编译器中国开发团队共同撰写,权威性毋庸置疑.系统.深 ...

  6. iOS - Swift Swift 语言新特性

    1.Swift 2.0 带来哪些新变化 常规变化: 1.OS X 10.11.iOS 9 和 watchOS 2 SDK 采纳了一些 Objective-C 的特性用来提高 Swift 的编程体验, ...

  7. Swift 3 新特性

    原文:What's New in Swift 3? ,作者:Ben Morrow,译者:kmyhy Swift 3将于今年下半年推出,为Swift开发者们带来了很多核心代码的改变.如果你没有关注过 S ...

  8. Apache入门篇(三)之apache2.4.33的新特性解析与虚拟主机实战

    1.http 2.4新特性 新特性: (1) 在编译时可以将多个MPM构建为可加载模块,可以在运行时通过LoadModule指令配置所选的MPM: (2) 2.2版本的event MPM在实验阶段,到 ...

  9. [Android 新特性] 改进明显 Android 4.4系统新特性解析

    Android 4.3发布半年之后,Android 4.4随着新一代Nexus5一起出现在了用户的面前,命名为从之前的Jelly Bean(果冻豆)换成了KitKat(奇巧).这个新系统究竟都有怎样的 ...

随机推荐

  1. 使用HTML5拍照

    原文连接地址: Camera and Video Control with HTML5 演示地址: HTML5拍照演示 翻译日期: 2013年8月6日 首先,我们看看HTML代码结构,当然,这部分的D ...

  2. FORM中的MOAC控制

    1.创建表时,对_ALL表创建同义词 -- Create Multi Org Synonym CREATE  OR REPLACE  SYNONYM CUX_WF_DEF_HEADER FOR CUX ...

  3. 【一天一道LeetCode】#82. Remove Duplicates from Sorted List II

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Given a ...

  4. iOS中 FMDB第三方SQLite数据库 UI_20

    1.什么是FMDB? FMDB是iOS平台下SQLite数据库,只不过它是OC方式封装了C语言的SQLite语句,使用起来更加面向对象 2.FMDB的优点:1.使用起来更加面向对象; 2.对比苹果自带 ...

  5. iOS中 数据持久化 UI高级_17

    数据持久化的本质就是把数据由内写到本地(硬盘中),在iOS指将数据写到沙盒文件夹下: 沙盒机制:指的就是采用沙盒文件夹的形式管理应用程序的本地文件,而且沙盒文件夹的名字是随机分配的,采用十六进制方法命 ...

  6. [asp.net]登录协同工作平台安全解决方案

    [摘要]公司领导说登录验证的安全性如何保证,建议采用UKEY验证类似网银解决,调用第三方YT公司产品. 解决方案: 前端页面: <embed id="s_simnew61" ...

  7. hadoop集群崩溃,因为tmp下/tmp/hadoop-hadoop/dfs/name文件误删除

    hadoop执行start-all后,显示正常启动. starting namenode, logging to /opt/hadoop-0.20.2-cdh3u0/logs/hadoop-hadoo ...

  8. tomcat的realm域

    Realm域,其实可以看成是一个包含了用户及密码的数据库,而且每个用户还会包含了若干角色.也就是包含了用户名.密码.角色三个列的数据记录集合,如下图,最下面椭圆内的包含的整块即可以看成realm域.它 ...

  9. Java实现栈之计算器

    Java实现栈来做一个将中缀表达式转化为后缀表达式的程序,中缀表达式更符合我们的主观感受,后缀表达式更适合计算机的运算,下面直接上代码吧: package Character1; import jav ...

  10. 使用Android Support Design 控件TabLayout 方便快捷实现选项卡功能

    1.概述 TabLayout是在2015年的google大会上,google发布了新的Android Support Design库的新组件之一,以此来全面支持Material Design 设计风格 ...