泛型能根据自定义的需求,编写出适用于任意类型的、灵活可复用的函数及类型。
泛型函数
泛型版本的函数使用占位符类型名(这里叫做 T ),而不是 实际类型名(例如 Int、String 或 Double),占位符类型名并不关心 T 具体的类型,但它要求 a 和b 必须是相同的类型,T 的实际类型由每次调用 swapTwoValues(:😃 来决定。
//泛型函数
func swapTwoValues<T>(_ a: inout T, _ b: inout T){
let temporaryA = a
a = b
b = temporaryA
}
类型参数
占位类型 T 是一个类型参数的例子,类型参数指定并命名一个占位类型,并且紧随在函数名后面,使用一对尖括号括起来(例如< T >)。
命名参数
类型参数具有描述下的名称,例如字典 Dictionary<Key, Value> 中的 Key 和 Value 及数组 Array 中的 Element,这能告诉阅读代码的人这些参数类型与泛型类型或函数之间的关系。
泛型类型
泛型函数,自定义泛型类型。
//泛型类型
struct Stack<Element> {
var items = [Element]()
mutating func push(_ item: Element){
items.append(item)
}
mutating func pop(_ item: Element) -> Element{
return items.removeLast()
}
}
泛型扩展
当对泛型类型进行扩展时,并不需要提供类型参数列表作为定义的一部分。原始类型定义中声明的类型参数列表在扩展中可以直接使用,并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用。
//泛型扩展
extension Stack {
var topItem: Element? {
return items.isEmpty ? nil : items[items.count - 1]
}
}
类型约束
类型约束指定类型参数必须继承自指定类、遵循特定的协议或协议组合。
//类型约束
func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) ->Int?{
for (index, value) in array.enumerated() {
if value == valueToFind{
return index
}
}
return nil
}
关联类型
定义一个协议时,声明一个或多个关联类型作为协议定义的一部分将会非常有用。关联类型为协议中的某个类型提供了一个占位符名称,其代表的实际类型在协议被遵循时才会被指定。关联类型通过 associatedtype 关键字来指定。
扩展现有类型来指定关联类型
给关联类型添加约束
在关联类型约束里使用协议
泛型 Where 语句
通过泛型 where 子句让关联类型遵从某个特定的协议,以及某个特定的类型参数和关联类型必须类型相同。
func allItemsMatch<C1: Container, C2: Container>
(_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.Item == C2.Item, C1.Item: Equatable {
// 检查两个容器含有相同数量的元素
if someContainer.count != anotherContainer.count {
return false
}
// 检查每一对元素是否相等
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
// 所有元素都匹配,返回 true
return true
}
- 具有泛型 Where 子句的扩展
- 具有泛型 Where 子句的关联类型
- 泛型下标
不透明类型
具有不透明返回类型的函数或方法会隐藏返回值的类型信息。函数不再提供具体的类型作为返回类型,而是根据它支持的协议来描述返回值。在处理模块和调用代码之间的关系时,隐藏类型信息非常有用,因为返回的底层数据类型仍然可以保持私有。而且不同于返回协议类型,不透明类型能保证类型一致性 —— 编译器能获取到类型信息,同时模块使用者却不能获取到。
不透明类型和泛型相反。泛型允许调用一个方法时,为这个方法的形参和返回值指定一个与实现无关的类型。
不透明类型和协议类型的区别
就在于是否需要保证类型一致性。一个不透明类型只能对应一个具体的类型,即便函数调用者并不能知道是哪一种类型;协议类型可以同时对应多个类型,只要它们都遵循同一协议。总的来说,协议类型更具灵活性,底层类型可以存储更多样的值,而不透明类型对这些底层类型有更强的限定。
自动引用计数
自动引用计数(ARC)机制来跟踪和管理你的应用程序的内存。
引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递。
类实例之间的循环强引用
循环强引用会一直阻止类实例的销毁,这就在应用程序中造成了内存泄漏。
解决实例之间的循环强引用
弱引用(weak)
ARC 会在引用的实例被销毁后自动将其弱引用赋值为 nil。并且因为弱引用需要在运行时允许被赋值为 nil,所以它们会被定义为可选类型变量,而不是常量。
无主引用(unowned)
无主引用在其他实例有相同或者更长的生命周期时使用,使用无主引用,必须确保引用始终指向一个未销毁的实例。
如果试图在实例被销毁后,访问该实例的无主引用,会触发运行时错误。
闭包的循环强引用
虽然闭包多次使用了 self,它只捕获实例的一个强引用。
解决闭包的循环强引用
定义捕获列表,弱引用和无主引用:
- 在闭包和捕获的实例总是互相引用并且总是同时销毁时,将闭包内的捕获定义为 无主引用。
- 在被捕获的引用可能会变为 nil 时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为 nil。这使我们可以在闭包体内检查它们是否存在。
注:如果被捕获的引用绝对不会变为 nil,应该用无主引用,而不是弱引用。
内存安全
内存访问冲突
- In-Out 参数的访问冲突
- 方法里 self 的访问冲突
- 属性的访问冲突
访问控制
访问级别
- open 和 public 级别可以让实体被同一模块源文件中的所有实体访问,在模块外也可以通过导入该模块来访问源文件里的所有实体。
- internal 级别让实体被同一模块源文件中的任何实体访问,但是不能被模块外的实体访问。
- fileprivate 限制实体只能在其定义的文件内部访问。
- private 限制实体只能在其定义的作用域,以及同一文件内的 extension 访问。
open 为最高访问级别(限制最少),private 为最低访问级别(限制最多)。
访问控制语法
通过修饰符 open、public、internal、fileprivate、private 来声明实体的访问级别。
自定义访问控制
元组,函数,枚举,嵌套,子类,常量、变量、属性、下标,构造器,协议,扩展,泛型,类型别名。