这篇是对一文鉴定是Swift的王者,还是青铜文章中问题的解答。这些问题仅仅是表层概念,属于知识点,在我看来即使都很清楚也并不能代表上了王者,如果非要用段位类比的话,黄金还是合理的😄。
Swift是一门上手容易,但是精通较难的语言。即使下面这些内容都不清楚也不妨碍你开发业务需求,但是了解之后它能够帮助我们写出更加Swifty的代码。
一、 协议 Protocol
ExpressibleByDictionaryLiteral
ExpressibleByDictionaryLiteral
是字典的字面量协议,该协议的完整写法为:
public protocol ExpressibleByDictionaryLiteral {
/// The key type of a dictionary literal.
associatedtype Key
/// The value type of a dictionary literal.
associatedtype Value
/// Creates an instance initialized with the given key-value pairs.
init(dictionaryLiteral elements: (Self.Key, Self.Value)...)
}
复制代码
首先字面量(Literal)的意思是:用于表达源代码中一个固定值的表示法(notation)。
举个例子,构造字典我们可以通过以下两种方式进行:
// 方法一:
var countryCodes = Dictionary<String, Any>()
countryCodes["BR"] = "Brazil"
countryCodes["GH"] = "Ghana"
// 方法二:
let countryCodes = ["BR": "Brazil", "GH": "Ghana"]
复制代码
第二种构造方式就是通过字面量方式进行构造的。
其实基础类型基本都是通过字面量进行构造的:
let num: Int = 10
let flag: Bool = true
let str: String = "Brazil"
let array: [String] = ["Brazil", "Ghana"]
复制代码
而这些都有对应的字面量协议:
ExpressibleByNilLiteral // nil字面量协议
ExpressibleByIntegerLiteral // 整数字面量协议
ExpressibleByFloatLiteral // 浮点数字面量协议
ExpressibleByBooleanLiteral // 布尔值字面量协议
ExpressibleByStringLiteral // 字符串字面量协议
ExpressibleByArrayLiteral // 数组字面量协议
复制代码
Sequence
Sequence翻译过来就是序列,该协议的目的是一系列相同类型的值的集合,并且提供对这些值的迭代能力,这里的迭代可以理解为遍历,也即for-in
的能力。可以看下该协议的定义:
protocol Sequence {
associatedtype Iterator: IteratorProtocol
func makeIterator() -> Iterator
}
复制代码
Sequence
又引入了另一个协议IteratorProtocol
,该协议就是为了提供序列的迭代能力。
public protocol IteratorProtocol {
associatedtype Element
public mutating func next() -> Self.Element?
}
复制代码
我们通常用for-in
实现数组的迭代:
let animals = ["Antelope", "Butterfly", "Camel", "Dolphin"]
for animal in animals {
print(animal)
}
复制代码
这里的for-in
会被编译器翻译成:
var animalIterator = animals.makeIterator()
while let animal = animalIterator.next() {
print(animal)
}
复制代码
Collection
Collection译为集合,其继承于Sequence。
public protocol Collection : Sequence {
associatedtype Index : Comparable
var startIndex: Index { get }
var endIndex: Index { get }
var isEmpty: Bool { get }
var count: Int { get }
subscript(position: Index) -> Element { get }
subscript(bounds: Range<Index>) -> SubSequence { get }
}
复制代码
是一个元素可以反复遍历并且可以通过索引的下标访问的有限集合,注意Sequence
可以是无限的,Collection
必须是有限的。
Collection
在Sequence
的基础上扩展了下标访问、元素个数能特性。我们常用的集合类型Array
,Dictionary
,Set
都遵循该协议。
CustomStringConvertible
这个协议表示自定义类型输出的样式。先来看下它的定义:
public protocol CustomStringConvertible {
var description: String { get }
}
复制代码
只有一个description
的属性。它的使用很简单:
struct Point: CustomStringConvertible {
let x: Int, y: Int
var description: String {
return "(\(x), \(y))"
}
}
let p = Point(x: 21, y: 30)
print(p) // (21, 30)
//String(describing: <#T##CustomStringConvertible#>)
let s = String(describing: p)
print(s) // (21, 30)
复制代码
如果不实现CustomStringConvertible
,直接打印对象,系统会根据默认设置进行输出。我们可以通过CustomStringConvertible
对这一输出行为进行设置,还有一个协议是CustomDebugStringConvertible
:
public protocol CustomDebugStringConvertible {
var debugDescription: String { get }
}
复制代码
跟CustomStringConvertible
用法一样,对应debugPrint
的输出。
Hashable
我们常用的Dictionary
,Set
均实现了Hashable
协议。Hash的目的是为了将查找集合某一元素的时间复杂度降低到O(1),为了实现这一目的需要将集合元素与存储地址之间建议一种尽可能一一对应的关系。
我们再看Hashable`协议的定义:
public protocol Hashable : Equatable {
var hashValue: Int { get }
func hash(into hasher: inout Hasher)
}
public protocol Equatable {
static func == (lhs: Self, rhs: Self) -> Bool
}
复制代码
注意到func hash(into hasher: inout Hasher)
,Swift 4.2 通过引入 Hasher
类型并采用新的通用哈希函数进一步优化 Hashable
。
如果你要自定义类型实现 Hashable</