介绍
==
为结构相等(需要实现 Equatable
协议),===
为指针相等或者引用相等:
class Foo: Equatable {
var bar: Int = 0
static func == (lhs: Foo, rhs: Foo) -> Bool {
lhs.bar == rhs.bar
}
}
let f1 = Foo()
let f2 = Foo()
let falseValue = (f1 === f2) // false
let trueValue = (f1 == f2) // true
- Foundation 框架中的
Data
结构体是对引用类型 NSData
的封装,当 Data
将对其封装的 NSData
对象进行深拷贝:
// Foundation
@frozen public struct Data {
// ...
}
var d1 = "kingcos.me".data(using: .utf8)
var d2 = d1
d1?.removeAll()
print(d1!.count) // 0
print(d2!.count) // 10
- 数组等集合类型只有当其中的元素满足值语义,其本身才具有值语义:
class Foo {
var bar: Int = 0
}
let f = Foo()
let arr1 = [f]
let arr2 = arr1
arr2[0].bar = 100 // arr1[0].bar == 100
// ---
struct Baz {
var bar: Int = 0
}
let b = Baz()
var arr3 = [b] // ⚠️ 这里需要使用 var
let arr4 = arr3
arr3[0].bar = 100 // arr4[0].bar == 0
final
仅能够修饰类以及类中的方法、属性,用以确保其不被子类化:
final class Foo { // 不得子类化
final func finalFunc() {} // 子类不得重写
final var finalVar = 0 // 子类不得重写
}
- 高阶函数(High-order function):接受别的函数作为参数的函数。
- 方法(Method):定义在类或者协议中的函数,具有一个隐式参数
self
;不是方法的函数有时称作自由函数(Free function)。
- 对于静态派发(Statically dispatched)的调用,编译器可能会内联优化。
- 类和协议上的方法可能是动态派发(Dynamically dispatched),编译器在编译时不需要知道哪个方法将被调用。在 Swift 中,动态特性由 vtable(虚表)或
selector
和 objc_msgSend
来完成,前者的处理方式类似 Java 或 C++,后者则只针对 @objc
修饰的类和协议上的方法。
- 函数重载和泛型中的方法在编译时确定,方法重写在运行时确定。
内建集合类型
- 可以使用
reduce(_:)
来实现 map
和 filter
,但需要注意这里的性能问题:在闭包中每次为数组追加元素时,都需要重新创建数组并复制元素,再叠加 reduce(_:)
本身 O(n)
,最终时间复杂度将为 O(n^2)
;这里也可使用 reduce(inout:)
避免数组重复创建和复制带来的损耗,重新将时间复杂度降回 O(n)
;另可参考《Arrays, Linked Lists and Performance》。
extension Array {
// O(n^2)
func rdc_map<T>(_ transform: (Element) -> T) -> [T] {
reduce([]) { $0 + [transform($1)] }
}
// O(n^2)
func rdc_filter(_ isIncluded: (Element) -> Bool) -> [Element] {
reduce([]) { isIncluded($1) ? $0 + [$1] : $0 }
}
// O(n)
func opt_rdc_filter(_ isIncluded: (Element) -> Bool) -> [Element] {
reduce(into: []) {
if isIncluded($1) { $0.append($1) }
}
}
}
flatMap
通过使用 append(contentsOf:)
(参数为 Sequence
代表序列)代替了 map
的 append(_:)
(参数为 Any
代表元素)实现了降维。
- 切片(Slice)类型是数组的一种表示方式,其背后仍然为原数组,因此也不会引起元素复制;操作切片时建议使用
startIndex
和 endIndex
:
let arr = [1, 2, 3]
let slice = arr[2...] // [3]
// slice[0] // Fatal error: Index out of bounds
slice[slice.startIndex] // 3
var dict1 = ["web": "github.com/kingcos", "wxpub": "萌面大道"]
var dict2 = ["web": "kingcos.me"]
// ["web": "kingcos.me", "wxpub": "萌面大道"]
dict1.merge(dict2) { $1 }
// [2: "b", 3: "c", 1: "a"]
let dict3 = Dictionary(uniqueKeysWithValues: zip([1, 2, 3], ["a", "b", "c"]))
// ["web": "v: github.com/kingcos", "wxpub": "v: 萌面大道"]
let dict4 = dict1.mapValues { return "v: \($0)" }
➜ ~ swift
Welcome to Apple Swift version 5.4.2 (swiftlang-1205.0.28.2 clang-1205.0.19.57).
Type :help for assistance.
1> 10.hashValue
$R0: Int = 8376577764884252349
[1] + 75023 suspended swift
➜ ~ swift
Welcome to Apple Swift version 5.4.2 (swiftlang-1205.0.28.2 clang-1205.0.19.57).
Type :help for assistance.
1> 10.hashValue
$R0: Int = -5587253586683290564
- 不建议使用引用类型变量作为字典的键,因为当键发生改变时,其哈希值和(或)相等性也会被改变,将无法再在字典中找到:
class Foo: Hashable {
var f: Int = 0
static func == (lhs: Foo, rhs: Foo) -> Bool {
lhs.f == rhs.f
}
func hash(into hasher: inout Hasher) {
hasher.combine(f)
}
}
let foo = Foo()
foo.f = 1
let dict = [foo: "foo"] // dict[foo] == Optional("foo")
foo.f = 100 // dict[foo] == nil
IndexSet
相比 Set
更加高效,可以存储一组范围列表:
var indices = IndexSet()
indices.insert(integersIn: 0..<3) // 0, 1, 2
indices.insert(integersIn: 5...10) // 5, 6, 7, 8, 9, 10
let oddsIndices = indices.filter { $0 % 2 != 0 } // 0:1, 1:5, 2:7, 3:9
- 可以利用
Set
的元素不重复性为 Sequence
扩展以下计算属性,注意这里仍然返回的是数组,即顺序是保证的:
print([1, 2, 3, 2, 4, 5].unique) // [1, 2, 3, 4, 5]
extension Sequence where Element: Hashable {
var unique: [Element] {
var s: Set<Element> = []
return filter { e in
if s.contains(e) {
return false
} else {
s.insert(e) // 存储首个出现的元素
return true
}
}
}
}