最新在写swift的时候感觉不怎么顺手,于是把新特性看了一遍,顺便做一下笔记,加深一下记忆。
1、语法改进
extesion 中可以访问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的协议。为了更swift话一下,可以将代码改造成。
struct Date {
private let secondsSinceReferenceDate: Double // 结构体中定一个私有属性
}
// 扩展结构体,实现Equatable,Equatable协议
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
}
}
swift4能这样调用,但是swift3将会提示错误,要把Date中的private改成fileprivate,这样会把作用域变大,造成属性的滥用。而swift4,就是将private的作用域变大,扩展到extension中,这样就不必更改属性的作用域了。
类型+协议 生成的组合类型
protocol Shakeable {
func shake()
}
extension UIButton: Shakeable {
func shake() {
print("a")
}
}
extension UISlider: Shakeable {
func shake() {
print("b")
}
}
func shakeEm(controls: [UIControl & Shakeable]) {
for control in controls where control.isEnabled {
control.shake()
}
}
这样就可以直接使用类型中遵循协议的方法,而不必要再判断cotrol是否遵循这个协议,然后在as一下来调用协议中的方法。
associatedtype 可以追加where的约束
protocol Shakeable {
associatedtype Element where Self.Element == Self.Iterator.Element
func shake()
}
这样可以避免在使用Shakeable时多做一个类型判断
key paths 语法的改变
因为以前的写法,这个在swift4上总是写的不舒服。swift4创建一个keyPath要用\开头
\keyPath
swift3使用setValue来写
@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)
而变成swift4就用了
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"
不是很习惯。
swift4的keyPath新特点:
1、可以定义class、struct,而不必加上@objcMembers、dynamic的关键字
2、类型安全和类型推断
3、可以用在所有的值类型上
下标支持泛型
现在下标用泛型不需要用as来转换类型了
例:
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" : "xx"])
let name: String? = dictionary["name"]
字符串
Unicode字符串在计算count时的正确性改善
var family = "👩"
print(family.characters.count) // 1
去掉characters
现在string的某些属性可以不用characters。但是自动填充下有些加的characters的类型和不加的类型有些不同。
可以取单侧边界
新加语法糖...可以对字符串取单侧边界
let v = "hhgggdads"
let startSlicIndex = v.index(v.startIndex, offsetBy: 3)
let subvalue = v[startSlicIndex...]
String实现了Collection协议
因为实现了collection协议,所以现在可以调用collection 的方法
例:
//翻转字符串
let abc: String = "abc"
print(String(abc.reversed()))
//遍历
for i in abc {
print(i)
}
//map
_ = abc.map {
print($0.description)
}
//filter
let filterd = abc.filter { $0 == "b"}
//reduce
let result = abc.reduce("1") { (result, c) -> String in
print(result)
print(c)
return result + String(c)
}
print(result)
Substring
在 Swift 中,String 的背后有个 Owner Object 来跟踪和管理这个 String,String 对象在内存中的存储由内存其实地址、字符数、指向 Owner Object 指针组成。Owner Object 指针指向 Owner Object 对象,Owner Object 对象持有 String Buffer。当对 String 做取子字符串操作时,子字符串的 Owner Object 指针会和原字符串指向同一个对象,因此子字符串的 Owner Object 会持有原 String 的 Buffer。当原字符串销毁时,由于原字符串的 Buffer 被子字符串的 Owner Object 持有了,原字符串 Buffer 并不会释放,造成极大的内存浪费。
在 Swift 4 中,做取子串操作的结果是一个 Substring 类型,它无法直接赋值给需要 String 类型的地方。必须用 String() 包一层,系统会通过复制创建出一个新的字符串对象,这样原字符串在销毁时,原字符串的 Buffer 就可以完全释放了。
多行字符串字面量
swift4可以把字符串写在一对"""中,这样字符串就可以写成多行。
let joke = """
Q: Why does have in their name?
A: I don't know, why does have s in their name?
Q: Because otherwise they'd be called (punchline).
"""
标准库
Encoding and Decoding
swift4中引入Codable来解决对象持久话问题
struct Language: Codable {
var name: String
var version: Int
}
将language对象的实例持久化,只要language遵循Codable协议就好,然后encode成json或者PropertyList
//encode
let swift = Language(name:"swift",version: 4)
if let encode = try? JSONEncoder().encode(swift){
// 保存encode
}
//decode
if let decoded = try? JSONDecoder().decode(Language.self, from: encoded) {
print(decoded.name)
}
sequence的改进
protocol Sequence {
associatedtype Element
associatedtype Iterator: IteratorProtocol where Iterator.Element == Element
func makeIterator() -> Iterator
}
swift4的associatedtype支持追加where语句,这样获取Sequence的元素类型可以不用 Iterator.Element,而是直接取 Element。
Protocol-oriented integers
整数类型符合的协议有修改,新增了 FixedWidthInteger 等协议,具体的协议继承关系如下
+-------------+ +-------------+
+------>+ Numeric | | Comparable |
| | (+,-,*) | | (==,<,>,...)|
| +------------++ +---+---------+
| ^ ^
+-------+------------+ | |
| SignedNumeric | +-+-------+-----------+
| (unary -) | | BinaryInteger |
+------+-------------+ |(words,%,bitwise,...)|
^ ++---+-----+----------+
| +-----------^ ^ ^---------------+
| | | |
+------+---------++ +---------+---------------+ +--+----------------+
| SignedInteger | | FixedWidthInteger | | UnsignedInteger |
| | |(endianness,overflow,...)| | |
+---------------+-+ +-+--------------------+--+ +-+-----------------+
^ ^ ^ ^
| | | |
| | | |
++--------+-+ +-+-------+-+
|Int family |-+ |UInt family|-+
+-----------+ | +-----------+ |
+-----------+ +-----------+
ps:个人也不是很理解,只是参看了文档上的解释。
Dictionary and Set enhancements
dictionary和set增强的功能:
1、通过Sequence来初始化
2、可以包含重复的Key
3、Filter的结果的类型和原类型一致
4、dictionary的mapValues方法
5、dictionary的默认值
6、dictionary可以分组
7、dictionary可以翻转
NSNumber bridging and Numeric types
let n = NSNumber(value: 999)
let v = n as? UInt8 // Swift 4: nil, Swift 3: 231
swift4中,把值为999的NSNumber转换为UInt8后会返回nil,而swift3会返回231这个意外的值
MutableCollection.swapAt(::)
MutableCollection 现在有了一个新方法 swapAt(::) 用来交换两个位置的值
var mutableArray = [1, 2, 3, 4]
mutableArray.swapAt(1, 2)
print(mutableArray)
// 打印结果:[1, 3, 2, 4]
工程上
预编译 Bridging Headers 文件
编译器会预编译 Bridging Headers 文件,这样就不要在编译的时候每次都编译oc的头文件生成的swift文件,从加快了编译速度。
Indexing 可以在编译的同时进行
用 Swift 开发项目时,近几个版本的 Xcode 进行 Indexing 的速度慢的令人发指。Xcode 9 和 Swift 4 在这方面做了优化,可以在编译的同时进行 Indexing,一般编译结束后 Indexing 也会同时完成
COW Existential Containers
Swift 中有个东西叫 Existential Containers,它用来保存未知类型的值,它的内部是一个 Inline value buffer,如果 Inline value buffer 中的值占用空间很大时,这个值会被分配在堆上,然而在堆上分配内存是一个性能比较慢的操作。
Swift 4 中为了优化性能引入了 COW Existential Containers,这里的 COW 就代表 “Copy-On-Write”,当存在多个相同的值时,他们会共用 buffer 上的空间,直到某个值被修改时,这个被修改的值才会被拷贝一份并分配内存空间。
移除未调用的协议实现
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
}
}
上面的例子,Date 实现了 Equatable 和 Comparable 协议。编译时如果编译器发现没有任何地方调用了对 Date 进行大小比较的方法,编译器会移除 Comparable 协议的实现,来达到减小包大小的目的。
减少隐式 @objc 自动推断
在 Swift 4 中,隐式 @objc 自动推断只会发生在很少的当必须要使用 @objc 的情况,比如:
复写父类的 Objective-C 方法
符合一个 Objective-C 的协议
其它大多数地方必须手工显示的加上 @objc。
减少了隐式 @objc 自动推断后,Apple Music app 的包大小减少了 5.7%。
OK笔记就到这,下面是参考资料:
WWDC 2017 Session 402 《What’s New in Swift》
WWDC 2017 Session 212 《What’s New in Foundation》
WWDC 2017 Session 102 《Platforms State of the Union》
《Swift Language Programming (Swift 4.0)》
https://github.com/apple/swift-evolution
https://github.com/ole/whats-new-in-swift-4
https://www.raywenderlich.com/163857/whats-new-swift-4
https://www.hackingwithswift.com/swift4