文 / 菲拉兔
要求:
- Platform: iOS8.0+
- Language: Swift4.0
- Editor: Xcode9
Migeration
-
Xcode9中进行代码升级迁移也很方便,比较智能化。
APIChanges
Strings
- 字符串在Swift4中可以直接遍历其中的字符,而不是通过characters
// Swift4
let greeting = "Hello Swift4.0"
greeting.forEach{
print($0)
}
// Before
let greeting = "Hello Swift"
greeting.characters.forEach{
print($0)
}
- 也可以使用Sequence和Collection协议的一些操作
let greeting = "Hello Swift"
// Swift4
greeting.count
greeting.isEmpty
greeting.dropFirst()
// Before
greeting.characters.count
- 子串有了新的类型
let greeting = "Hello Swift"
let endIndex = greeting.index(greeting.startIndex, offsetBy: 5)
var hello = greeting[greeting.startIndex...endIndex] // `Hello`
type(of: hello) // Substring.Type
// 子串的操作和String一样
hello += " World"
let helloString = String(hello) // `Hello World`
Dictionary & Set
- Swift4中对于Collection和Dictionary的转换有了更方便灵活的操作
- Array和Dictionary转换。keys和values数量一致
let keys = ["name", "age", "isMale"]
let values = ["jack", 18, true] as [Any]
let dict = Dictionary(uniqueKeysWithValues: zip(keys, values))
// 结果
["name": "jack", "age": 18, "isMale": true]
- 如果keys和values数量不一致,按数量少的匹配
let keys = ["name", "age", "isMale"]
let values = ["jack", 18] as [Any]
// 结果
["name": "jack", "age": 18]
Dictionary Default Values
- 有时会碰到字典中的值为nil时,需要设置为默认的值,但在Swift4中有了新的方式
let dict = ["name": nil, "age": Optional(18)]
// Before Swift4
let name = dict["name"] ?? "Unkown Name"
let nickname = dict["nickname"] ?? "Unkown NickName"
// In Swift4
// 没有找到key值时,value为default值
let nickname = dict["nickname", default: "Unkown NickName"]
Dictionary Grouping
- 这个感觉很有用,比如统计一个单词的各个字母出现的次数
let grouped = Dictionary.init(grouping: "Hello World", by: { $0 })
grouped.keys.forEach { print("\($0) : \(grouped[$0]!.count)") }
//结果:
H : 1
r : 1
e : 1
o : 2
l : 3
: 1
W : 1
d : 1
Private Access Modifier
- Swift4中private属性在extension中可以访问
struct Person{
private let name: String
init(name: String) {
self.name = name
}
}
extension Person{
func call(somebodyWith name: String) {
if name == self.name{
print("I am!")
}
else{
print("\(name) is not me!")
}
}
}
let xiaoming = Person(name: "Xiao Ming")
xiaoming.call(somebodyWith: "Xiao Ming")
API Additions
Archival and Serialization
- Swift4以前,通常要对一个object进行归/解档的话,首先要让对象成为NSObject的子对象,并遵守NSCoding协议,然后进行一系列的处理;
- Swift4中,通常只需要让object和其子属性类型遵守Codable协议即可
struct Person: CustomStringConvertible, Codable{
enum Role: String, Codable {
case student, teacher
}
var name: String
var age: Int
var role: Role
var description: String{
return "(name: \(name), age: \(age), role: \(role.rawValue))"
}
}
let student = Person(name: "Xiao Ming", age: 18, role: .student)
// 编码
guard let data = try? JSONEncoder().encode(student),
let jsonText = String(data: data, encoding: .utf8) else{
fatalError("JSON Object Encode Failed")
}
print(jsonText)
// 结果:
{"name":"Xiao Ming","age":18,"role":"student"}
// 解码
guard let xiaoming = try? JSONDecoder().decode(Person.self, from: data) else{
fatalError("JSON Object Decode Failed")
}
print(xiaoming)
// 结果:
(name: Xiao Ming, age: 18, role: student)
- 关于JSON编码/解码的详细用法,可参看我另一篇专题《Swift 4.0 | JSON数据的解析和编码》
Key-Value Coding
- KVO在OC中比较一般用来深层遍历对象的属性,或设置新的值。在Swift4中,它称为一种类型
WritableKeyPath<Any, Any>
而不是以前的字符串,表现方式也有了全新的形式:\Any.propertyName
。
struct School{
var name: String
var address: String
}
struct Person{
enum Role: String {
case student, teacher
}
var name: String
var age: Int
var role: Role
var school: School
}
var student = Person(name: "Xiao Ming", age: 18, role: .student, school: School(name: "BISTU", address: "Beijing"))
// 创建name key path实例
let nameKeyPath = \Person.name // `WritableKeyPath<Person, String>`
// 取值
let name = student[keyPath: nameKeyPath] // `Xiao Ming`
// 也可以进一步取值
let schoolName = student[keyPath: \Person.school].name // `BISTU`
// 赋值
student[keyPath: \Person.school].name = "TingHua" // `TingHua`
Multi-line String Literals
- Swift4中终于支持多行字符串了,这在写JSON/XML/HTML等格式化的文本时,显得尤为方便。Swift4中用
""" xxx """
三个双引号表示多行文本。
// String类型
let JSONText = """
[
{
"name": "Banana",
"points": 200,
"description": "A banana grown in Ecuador."
}
]
"""
One-Sided Ranges
- 单边范围。在Swift4以前,定义一个范围Range,基本都需要指定startIndex和endIndex,但在Swift4中,系统可以自动推断该range的start/endIndex,减少了冗余,提高了可读性。
let numbers = [1, 2, 3, 4]
numbers[1...]
numbers[..<2]
numbers[1..<2]
- 也允许你定义无限大的range
let infiniteRange = 1... // CountablePartialRangeFrom<Int>类型
infiniteRange.forEach{
print($0)
}
- 单边range在模式匹配中的使用
func markLevelWith(score: UInt){
switch score {
case 90...100:
print("A")
case 80..<90:
print("B")
case 60..<80:
print("C")
case ..<60:
print("D")
default:
print("Your Score is Over the Top Level!")
}
}
markLevelWith(score: 33) // D
Generic Subscripts
- Swift4中,你可以为类型定义一个可以通用任何key的下标算式。
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 person = GenericDictionary(data: ["name": "Xiao Ming", "age": 18])
let name: String? = person["name"] // `Xiao Ming`
let age: Int? = person["age"] // `18`
- 而且还可以实现多个key的组合下标
extension GenericDictionary{
subscript<Keys: Sequence>(keys: Keys) -> [Value] where Keys.Iterator.Element == Key{
var values = [Value]()
for key in keys{
if let value = data[key] {
values.append(value)
}
}
return values
}
}
person[["name", "age"]] // ["Xiao Ming", 18]
MutableCollection.swapAt(::)
- 可变集合类型的值交换方法
// 简单来说
var info = person[["name", "age"]] // ["Xiao Ming", 18]
info.swapAt(0, 1) // [18, "Xiao Ming"]
Associated Type Constraints
- 关联类型约束
protocol MyProtocol {
associatedtype Element
associatedtype SubSequence : Sequence where SubSequence.Iterator.Element == Iterator.Element
}
Class & Protocol
协议组合
- 在Swift4中一个对象的属性可以遵守多个协议,用&组合
protocol Speakable{ }
protocol Eatable{ }
protocol Loveable{ }
protocol Moveable{ }
class Person: Speakable, Eatable, Loveable, Moveable{
}
class Pet{
var master: (Loveable & Moveable)?
}
let cat = Pet()
cat.master = Person()
Limiting @objc Inference
- 限制@objc的接口。Swift4中系统做了一些限制,避免了一些在使用该标记时给项目带来的风险和增加二进制文件的体积。
NSNumber Bridging
- NSNumber和基本数据类型的转换,在Swift4中也进行了简化,只有一种统一的转换方式
let number = NSNumber(value: 10)
let ok = NSNumber(value: true)
- 也修复了一些操作运算的错误
let n = NSNumber(value: 999)
let v = n as? UInt8 // Swift 4: nil, Swift 3: 231
let x = 2^8 // 10