swift4新功能
内容
开区间
字符
同 件内的扩展,私有声明可
智能Key path
编码和解码
协议相关类型的约束字典(Dictionary)和集合(Set)的增强MutableCollection.swapAt法reduce和inout
泛型下标
NSNumber桥接
类和协议的组合
开区间
SE-0172带来 种新的RangeExpression协议和 组前缀/后缀操作符给开区间.比如现在区间
论是上界还是下界都可以不指定.
限序
你可以 开区间来造 个 限序,对 期使enumerated()法的同学来说,这是 个福,尤
其是当你不想序号从0开始的时候:
let字 表= ["a","b","c","d"]
let加序号的字 表= zip(1...,字 表)
Array(加序号的字 表)集合的下标
在集合的下标中 开区间的话,集合的startIndex or endIndex会“智能填充”缺失的那 边.
let numbers = [1,2,3,4,5,6,7,8,9,10]
numbers[5...]取代numbers[5..
开区间可 于 式匹配,比如 个switch语 中case表达式.不过,编译器好像还不能(暂时?)判
定switch已被穷尽.
let value = 5
switch value {
case 1...:
print("于0")
case 0:
print("0")
case ..<0:
print("于0")
default:
fatalError("不可到达")
}
字符
多 字符 字
SE-0168带来 种简洁定义多 字符 的语法,使(""").在 个多 字符 并不需要写转义字
符,也就是说 多数 本格式(如JSON或HTML)就可以直接粘贴 须任何转义.结尾三引号的
缩进,决定 每 头部被裁剪的空格多少. Python:致敬我吗Kotlin:我也早有这功能
let多 字符= """
这是 个多 字符.不需要在这 转义"引号".结尾三引号的位置,控制空格的裁剪数.
"""
print(多 字符)
可以打开控制台(菜单View > Debug Area > Activate Console)来看print的输出.
字符"双"变回 个Collection,没错,天地暂停,时光倒流
SE-0163是Swift 4字符 模型的第 部分修正.最 变化String再度是 个Collection (因为在Swift 1.x中是这样的),比如String.CharacterView已经被并入其 类型中. (其他view,
UnicodeScalarView, UTF8View,和UTF16View,依旧存在.)
注意SE-0163还没完全实现并且这条建议中还有很多字符 相关的提议(意思是还有的折腾).
let欢迎语= "侬好Bobo, !"不需要再钻到.characters属性 去
欢迎语.count
for字in欢迎语{
print(字)
}
Substring是字符 切片后的新类型
字符 切片现在是Substring类型的实. String和Substring两者都遵从StringProtocol.乎所
有字符API都在StringProtocol所以String和Substring为很 程度是 样的.
let逗号的位置=欢迎语.index(of: ",")!
let substring =欢迎语[..<逗号的位置]
type(of: substring)
Substring可以调String的API
print(substring.uppercased())
Unicode 9
Swift 4即将 持Unicode 9,当前正在修正 些时髦emoji适当的语义问题.下 的所有字符计数
是1,和实际的对比:
"".count+肤"".count有4个成员的家庭
"\u{200D}\u{200D}\u{200D}".count家庭+肤"".count+肤+职业Character.unicodeScalars属性
现在可以直接访问 个Character的unicode编码值,不 先转成String (SE-0178):
let c: Character = ""
Array(c.unicodeScalars)同 件内的扩展,私有声明可
SE-0169改 访问控制规则,比如在同 件内的扩展中,原类型的private声明也是可 的.这种改
进可让同 件内保持使private分割类型定义成为可能,减少不受欢迎的fileprivate关键词的使.
struct SortedArray {
private var storage: [Element] = []init(unsorted: [Element]) {
storage = unsorted.sorted()
}
}
extension SortedArray {
mutating func insert(_ element: Element) {
storage此处可storage.append(element)
storage.sort()
}
}
let array = SortedArray(unsorted: [3,1,2])
storage此处不可(不像fileprivate)
array.storage error: 'storage' is inaccessible due to 'private' protection level智能key path
SE-0161描述的新式key path有可能搞 个Swift 4的 新闻.不像Cocoa中基于字符 的那样too
simple, Swift中的可是强类型的,你们要认真学习.
struct{
var名字: String
}
struct书{
var标题: String
var作者: []
var第 作者:{
return作者.first!
}
}
let Vergil =(名字: "Vergil")
let Xernaga =(名字: "Xernaga")
let Kotlin快速入 书=书(标题: "Kotlin快速入",作者: [Vergil, Xernaga])
Key path由 个根类型开始,和其下任意深度的属性链和下标名组成.
你可以写 个key path由 个反斜杠开始: \书.标题.每个类型 动获取 个[keyPath: ...]下标可
以设置或获取指定key path的值.
Kotlin快速入 书[keyPath: \书.标题]
Key path可深入并 持计算属性
Kotlin快速入 书[keyPath: \书.第 作者.名字]
Key path是可被存储和操作的对象.比如,你可以给 个key path加上额外字段深入到作者.
let作者KeyPath = \书.第 作者
type(of:作者KeyPath)
let名字KeyPath =作者KeyPath.appending(path: \.名字)可以省 类型名,如果编译器能推断的话
Kotlin快速入 书[keyPath:名字KeyPath]
下标Key path
Key paths也 持下标.如此 来可以非常 捷的深入到数组或字典这些集合类型中.不过这功能
在当前snapshot还未实现.
压缩化 和 序 化
SE-0166: Swift Archival & Serialization定义 种为任意Swift类型(class, struct,和enum)来描
述 如何压缩和序 化的 法.类型可遵从Codable协议让 可(解)压缩.多数情况下添加Codable协议就可以让你的 定义类型完美解压缩,因为编译器可以 成 个
默认的实现,前提是所有成员类型都是Codable的.当然你可以覆盖默认 法如果需要优化 定义
类型的编码.这个说来话—还请研读SE-0166.
遵从Codable协议,让 个 定义类型(和其所有成员)可压缩struct扑克: Codable {
enum全部花: String, Codable {
case桃,花,红,片
}
enum全部点数: Int, Codable {
case尖= 1,,三,四,五,六,七,八,九,,钩,蛋,老K
}
var花:全部花
var点数:全部点数}
let我的牌= [扑克(花: .桃,点数: .尖),扑克(花: .红,点数: .蛋)]编码
旦有 个Codable值,你要把它传递给 个编码器以 压缩.
Codable协议的基础设施可以写 的编解码器,不过Swift同时为JSON提供 个内置的编解
码器(JSONEncoder和JSONDecoder)和属性 表(PropertyListEncoder和PropertyListDecoder).这些是在SE-0167中定义的. NSKeyedArchiver同样 持所有的Codable类型.
import Foundation
var encoder = JSONEncoder()
JSONEncoder提供的可定制化属性encoder.dataEncodingStrategy
encoder.dateEncodingStrategy
encoder.nonConformingFloatEncodingStrategy
encoder.outputFormatting = .prettyPrinted格式化的json字符encoder.userInfo
let jsonData = try encoder.encode(我的牌)
String(data: jsonData, encoding: .utf8)
解码
let decoder = JSONDecoder()
let decoded = try decoder.decode([扑克].self, from: jsonData)协议相关类型的约束
SE-0142:协议的相关类型可以where语 约束.看似 步,却是类型系统表达能 的 步,让
标准库可以 幅简化.喜 普奔的是, Sequence和Collection在Swift 4中 上这个就 直观.
Sequence.Element
Sequence现在有 的相关类型Element .原先Swift 3中到处露脸的Iterator.Element ,现在
瘦 成Element:
extension Sequence where Element: Numeric {
var求和: Element {
var结果: Element = 0
for单个元素in self {结果+=单个元素
}
return结果}
}
[1,2,3,4].求和
当扩展Sequence和Collection时所需约束 少
在Swift 3时代,这种扩展需要很多的约束:
extension Collection where Iterator.Element: Equatable,
SubSequence: Sequence,
SubSequence.Iterator.Element == Iterator.Element
在Swift 4,编译器已经提前知道 上述3个约束中的2个,因为可以 相关类型的where语 来表
达它们.
extension Collection where Element: Equatable {
func头尾镜像(_ n: Int) Bool {
let头= prefix(n)
let尾= suffix(n).reversed()
return头.elementsEqual(尾)
}
}
[1,2,3,4,2,1].头尾镜像(2)
字典(Dictionary)和 集合(Set)的增强
SE-0165加 些很奶死的Dictionary和Set增强.基于序(Sequence)的构造器
从 个键值对序 构造字典.
let热 编程语= ["Swift", "Python", "Kotlin"]
let热 编程语 排= Dictionary(uniqueKeysWithValues: zip(1...,热 编程语))热 编程语 排[2]
合并(merge)构造器& merge法
当从 个序 构造字典,或把 个序 合并到字典中,描述如何处 重复的键.
let热 技术= [("苹果", "Swift"), ("歌", "TensorFlow"), ("苹果", "Swift Playgrouds"),
("苹果", "ARKit"), ("歌", "TensorFlowLite"),("歌", "Kotlin"),("苹果", "Core ML")]
let商= Dictionary(热 技术, uniquingKeysWith: { (第,最后) in最后})
商
合并构造器或merge法遇到 个字典时就没那么舒服.因为字典的元素类型是 个 带标签的
元组型(key: Key, value: Value)但上述2个 法却要求 个 标签的 元组型(Key, Value),不得已
要 转换.希望这个今后能完善.SR-922和SR-4969.
let默认设置= ["动登录": false, "已绑定 机": false, "蓝牙开启": false]
var户设置= ["动登录": true, "已绑定 机": false]
会产 个烦 的类型转换警告
let合并的设置=户设置.merge(默认设置) { (old, _) in old }
只能使 以下替代:
户设置.merge(默认设置.map { $0 }) { (old, _) in old }户设置
下标的默认值
你现在可以给下标中加 个默认值参数,当key不存在时会返回这个值,这样 可让返回类型非Optional.
热 编程语 排[4, default: "(未知)"]
/*:在你想通过下标 新 个值时,这个功能就非常有:
*/
import Foundation
var词组= """
天姥连天向天横 势拔五岳掩 城
天台四万八千丈 对此欲倒东南倾
我欲因之梦吴越 夜 度镜湖
湖 照我影 送我 剡溪
"""
var出现频率: [Character: Int] = [:]
for词in词组.components(separatedBy: .whitespacesAndNewlines).joined() {
出现频率[词, default: 0] += 1
}
for (词,次数) in出现频率{
if次数> 1 {
print(词,次数)
}
}
Dictionary相关的map和filter
filter返回 个Dictionary非Array.相似的,新 法mapValues转换值的同时保持字典结构.
let filtered =热 编程语 排.filter {
$0.key % 2 == 0
}
type(of: filtered)
let mapped =热 编程语 排.mapValues { value in
value.uppercased()
}
mapped
Set.filter现在同样返回 个Set不是Array.
let set: Set = [1,2,3,4,5]
let filteredSet = set.filter { $0 % 2 == 0 }type(of: filteredSet)
分组 个序
把 个序 分成 组,比如联系 按姓分组.
let联系= ["张三丰", "李思思", "张素芳", "李", "王", "张 军"]
let通讯录= Dictionary(grouping:联系, by: { $0.first! })
通讯录
SE-0173介绍 种交换 个集合中两个元素的新 法.与既有的swap(::)法不同, swapAt(::)接受 个要交换的元素切片,不是整个元素本(通过inout参数).
加这个的 的是swap法带2个inout参数 不再兼容新的独占式内存访问规则,SE-0176.既有
的swap(::)法不能再交换同 个集合中的两个元素.
var numbers = [1,2,3,4,5]
numbers.swapAt(0,1)
Swift 4中非法swap(&numbers[3], &numbers[4])numbers
reduce和inout
SE-0171新增reduce的 个变体,让部分结果以inout传递给组合函数.如此 来可以通过消除
中间结果的副本来递增 个序,幅提升reduce算法的性能.
SE-0171为实现.
尚未实现
extension Sequence where Element: Equatable {
func uniq() [Element] {
return reduce(into: []) { (result: inout [Element], element) in
if result.last != element {
result.append(element)
}
}
}
}
[1,1,1,2,3,3,4].uniq()
泛型下标
托SE-0148的福,下标现在可以有泛型参数和返回类型.
最权威的 莫过于表JSON数据:你可以定义 个泛型下标来保持调 者期望类型的内容.
struct JSON {
fileprivate var storage: [String:Any]
init(dictionary: [String:Any]) {
self.storage = dictionary
}
subscript(key: String) T? {
return storage[key] as? T
}
}
let json = JSON(dictionary: [
"城市名": "北京",
"国家代码": "cn",
"": 21_710_000
])
没必要as? Int
let population: Int? = json[""]
另 个: Collection的 个下标接受 个泛型索引序,并返回 个这些索引所在的数组:
extension Collection {
subscript(indices: Indices) [Element] where Indices.Element == Index {
var result: [Element] = []
for index in indices {
result.append(self[index])
}
return result
}
}
let words = "我 思 故 我 在".split(separator: " ")words[[1,2]]
NSNumber桥接
SE-0170修正部分危险 为当桥接Swift原 数字类型和NSNumber的时候.
import Foundation
let n = NSNumber(value: UInt32(301))
let v = n as? Int8 nil(Swift 4). Swift 3会是45 (试试看!).
类和协议的组合
SE-0156:你现在能写出OC这段UIViewController *在Swift中的等价代码,比如声明这样 个变
,这个变 拥有实体类型并同时遵守 协议.语法let变:某个类&协议1 &协议2.import Cocoa
protocol HeaderView {}
class ViewController: NSViewController {
let header: NSView & HeaderView
init(header: NSView & HeaderView) {
self.header = header
super.init(nibName: nil, bundle: nil)!
}
required init(coder decoder: NSCoder) {
fatalError("not implemented")
}
}
不能传 个简单的NSView进去因为不遵守协议ViewController(header: NSView())
错误: argument type 'NSView' does not conform to expected type 'NSView & HeaderView'必须穿 个NSView (类)同时遵守协议
extension NSImageView: HeaderView {}
ViewController(header: NSImageView())有