【Swift】Swift基础语法:面向对象、扩展、协议与泛型
一、Swift基础语法
1.13 继承与多态(Inheritance & Polymorphism)
类支持单继承,子类可重写父类的方法和属性,多态体现为“父类指针指向子类实例”。
1. 类的继承
- 语法:
class 子类名: 父类名 { ... },子类需调用父类初始化器(super.init())。
// 父类
class StudDetails {
var mark1: Int, mark2: Int
// 父类指定初始化器
init(stm1: Int, results stm2: Int) {
mark1 = stm1
mark2 = stm2
}
// 父类实例方法
func show() {
print("Mark1: \(mark1), Mark2: \(mark2)")
}
}
// 子类(继承 StudDetails)
class Tom: StudDetails {
// 子类初始化器:需调用父类初始化器(super.init)
init(stm1: Int, stm2: Int) {
super.init(stm1: stm1, results: stm2)
}
}
let tom = Tom(stm1: 100, stm2: 95)
tom.show() // 继承父类方法,输出:Mark1: 100, Mark2: 95
2. 方法重写(override)
子类重写父类方法需加 override 关键字,确保编译器检查重写合法性。
class SuperClass {
func show() {
print("这是超类 SuperClass")
}
}
class SubClass: SuperClass {
// 重写父类方法,必需加 override
override func show() {
print("这是子类 SubClass")
}
}
let subObj = SubClass()
subObj.show() // 输出:这是子类 SubClass
3. 属性重写
子类可重写父类的计算属性和属性观察器(存储属性不能重写,但可添加观察器)。
// 父类:计算属性
class Circle {
var radius = 12.5
var area: String {
return "圆半径 \(radius)"
}
}
// 子类:重写计算属性
class Rectangle: Circle {
var print = 7
override var area: String {
// 调用父类属性(super.area)
return super.area + ",但现在被重写为 \(print)"
}
}
// 子类:重写属性观察器
class Square: Rectangle {
// 重写父类的存储属性(添加观察器)
override var radius: Double {
didSet {
print = Int(radius / 5.0) + 1
}
}
}
let sq = Square()
sq.radius = 100.0 // 触发 didSet,print = 21
print(sq.area) // 输出:圆半径 100.0,但现在被重写为 21
4. final 关键字
用 final 修饰类、方法、属性,防止被继承或重写。
// final 类:不能被继承
final class FinalClass {
func show() {
print("FinalClass 不能被继承")
}
}
// final 方法:子类不能重写
class ParentClass {
final func printMsg() {
print("该方法不能被重写")
}
}
class ChildClass: ParentClass {
// override func printMsg() { ... } // 错误:final 方法不能重写
}
5. 多态
多态指“父类类型的变量可指向子类实例”,调用方法时实际执行子类的重写实现。
let baseClass: SuperClass = SubClass() // 父类指针指向子类实例
baseClass.show() // 执行子类重写的方法,输出:这是子类 SubClass
1.14 构造器与析构器(Initializer & Deinitializer)
构造器用于初始化实例,析构器用于释放实例资源(仅类支持)。
1. 构造器类型
(1)默认构造器(Default Initializer)
- 类/结构体无显式构造器时,Swift 自动生成默认构造器(无参数,初始化所有属性为默认值)。
class ShoppingListItem {
var name: String? = nil // 可选类型默认 nil
var quantity = 1 // 显式默认值
var purchased = false // 显式默认值
}
// 调用默认构造器
let item = ShoppingListItem()
print("名字: \(item.name ?? "无"), 数量: \(item.quantity)") // 输出:名字: 无, 数量: 1
(2)逐一构造器(Memberwise Initializer)
- 结构体无显式构造器时,Swift 自动生成逐一构造器(按属性顺序传入参数)。
struct Rectangle3 {
var length = 100.0, breadth = 200.0
}
// 调用逐一构造器(可指定部分参数,未指定用默认值)
let rect = Rectangle3(length: 24.0)
print("length = \(rect.length), breadth = \(rect.breadth)") // 输出:24.0, 200.0
(3)指定构造器(Designated Initializer)
- 类的主要构造器,需初始化所有属性,调用父类指定构造器(
super.init)。
class MainClass {
var no1: Int
// 指定构造器
init(no1: Int) {
self.no1 = no1
}
}
class SubClass: MainClass {
var no2: Int
// 子类指定构造器:先初始化自身属性,再调用父类指定构造器
init(no1: Int, no2: Int) {
self.no2 = no2
super.init(no1: no1)
}
}
(4)便利构造器(Convenience Initializer)
- 辅助构造器,用
convenience修饰,调用同一类的指定构造器(self.init),不能直接调用父类构造器。
class SubMainClass: MainClass {
var no2: Int
// 指定构造器
init(no1: Int, no2: Int) {
self.no2 = no2
super.init(no1: no1)
}
// 便利构造器:调用指定构造器
convenience init(no1: Int) {
self.init(no1: no1, no2: 0)
}
}
// 调用便利构造器
let obj = SubMainClass(no1: 10)
print("no1 = \(obj.no1), no2 = \(obj.no2)") // 输出:10, 0
(5)必要构造器(Required Initializer)
- 用
required修饰,子类必须实现该构造器(无论子类是否定义其他构造器),子类实现时也需加required。
class ClassA {
// 必要构造器:子类必须实现
required init() {
print("ClassA 初始化")
}
}
class ClassB: ClassA {
// 子类必须实现 required 构造器,且加 required(无需 override)
required init() {
print("ClassB 初始化")
}
}
let objA = ClassA() // 输出:ClassA 初始化
let objB = ClassB() // 输出:ClassB 初始化
2. 构造器代理(Initializer Delegation)
- 构造器调用同一类的其他构造器(值类型:
self.init;类:指定构造器调用父类指定构造器,便利构造器调用同一类指定构造器)。
// 结构体构造器代理(值类型)
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
// 构造器 1:默认构造器
init() {}
// 构造器 2:指定构造器
init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
// 构造器 3:便利构造器(代理到构造器 2)
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
// 调用构造器 3
let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0))
print("origin.x = \(centerRect.origin.x)") // 输出:2.5
3. 析构器(Deinitializer)
- 仅类支持,用
deinit修饰,实例被 ARC 释放前自动调用(无需手动调用),用于释放资源(如关闭文件、移除通知)。
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) 初始化")
}
deinit {
print("\(name) 被析构(释放)")
}
}
// 超出作用域时,析构器自动调用
func createPerson() {
let person = Person(name: "小明") // 初始化,输出:小明 初始化
}
createPerson() // 函数结束,person 被释放,输出:小明 被析构(释放)
1.15 自动引用计数(ARC)
ARC 是 Swift 管理内存的机制,自动跟踪实例的引用计数,引用计数为 0 时释放实例。
1. ARC 基本原理
- 强引用:默认引用类型,增加实例的引用计数(
+1),引用计数为 0 时实例被释放。 - 弱引用(
weak):不增加引用计数,适用于“可选类型的循环引用”(实例可能为 nil)。 - 无主引用(
unowned):不增加引用计数,适用于“非可选类型的循环引用”(实例永不为 nil)。
// 强引用示例
class Person {
let name: String
init(name: String) {
self.name = name
}
deinit {
print("\(name) 被释放")
}
}
var ref1: Person? = Person(name: "小红") // 引用计数 = 1
var ref2 = ref1 // 引用计数 = 2
var ref3 = ref1 // 引用计数 = 3
ref1 = nil // 引用计数 = 2
ref2 = nil // 引用计数 = 1
ref3 = nil // 引用计数 = 0,实例被释放,输出:小红 被释放
2. 循环强引用(Retain Cycle)
- 两个或多个实例互相持有强引用,导致引用计数无法为 0,实例永远不被释放(内存泄漏)。
(1)类之间的循环引用
class Person {
var apartment: Apartment?
deinit {
print("Person 被释放")
}
}
class Apartment {
var tenant: Person?
deinit {
print("Apartment 被释放")
}
}
var person: Person? = Person()
var apartment: Apartment? = Apartment()
person?.apartment = apartment // Person 持有 Apartment 强引用
apartment?.tenant = person // Apartment 持有 Person 强引用
// 即使设为 nil,引用计数仍为 1(互相持有),实例不被释放
person = nil
apartment = nil // 无输出(未调用 deinit)
(2)解决循环引用:弱引用(weak)
用 weak 修饰其中一个引用(需为可选类型),打破循环。
class Person {
var apartment: Apartment?
deinit {
print("Person 被释放")
}
}
class Apartment {
weak var tenant: Person? // 弱引用(可选类型)
deinit {
print("Apartment 被释放")
}
}
var person: Person? = Person()
var apartment: Apartment? = Apartment()
person?.apartment = apartment
apartment?.tenant = person
person = nil // 引用计数 = 0,Person 被释放,输出:Person 被释放
apartment = nil // 引用计数 = 0,Apartment 被释放,输出:Apartment 被释放
(3)解决循环引用:无主引用(unowned)
无主引用适用于 “两个实例生命周期绑定,且被引用的实例永不为 nil” 的场景(如 “主人 - 宠物” 中,宠物的主人永不为 nil)。无主引用非可选类型,无需解包,但如果实例被释放后访问无主引用,会触发运行时崩溃。
class Student {let name: Stringvar section: Marks? // 学生可能没有分数(可选)init(name: String) {self.name = name
}deinit {print("\(name) 被释放")}}class Marks {let marks: Intunowned let stname: Student // 分数的学生永不为 nil(无主引用)init(marks: Int, stname: Student) {self.marks = marks
self.stname = stname
}deinit {print("分数 \(marks) 被释放")}}// 实例生命周期绑定:Student 存在时,Marks 才存在var student: Student? = Student(name: "小明")
student?.section = Marks(marks: 98, stname: student!) // Marks 持有 Student 无主引用
student = nil // Student 被释放,Marks 也随之释放// 输出:// 小明 被释放// 分数 98 被释放
2. 循环强引用(Retain Cycle)(续)
(3)解决循环引用:无主引用(unowned)
无主引用适用于“两个实例生命周期绑定,且被引用的实例永不为 nil”的场景(如“主人-宠物”中,宠物的主人永不为 nil)。无主引用非可选类型,无需解包,但如果实例被释放后访问无主引用,会触发运行时崩溃。
class Student {
let name: String
var section: Marks? // 学生可能没有分数(可选)
init(name: String) {
self.name = name
}
deinit {
print("\(name) 被释放")
}
}
class Marks {
let marks: Int
unowned let stname: Student // 分数的学生永不为 nil(无主引用)
init(marks: Int, stname: Student) {
self.marks = marks
self.stname = stname
}
deinit {
print("分数 \(marks) 被释放")
}
}
// 实例生命周期绑定:Student 存在时,Marks 才存在
var student: Student? = Student(name: "小明")
student?.section = Marks(marks: 98, stname: student!) // Marks 持有 Student 无主引用
student = nil // Student 被释放,Marks 也随之释放
// 输出:
// 小明 被释放
// 分数 98 被释放
(4)闭包引起的循环引用
当类的属性持有闭包,且闭包内部捕获 self(类实例)时,会形成“类实例 → 闭包 → 类实例”的循环强引用。需通过 捕获列表(Capture List) 打破循环。
class HTMLElement {
let name: String
let text: String?
// 类属性持有闭包(强引用)
lazy var asHTML: () -> String = {
// 闭包捕获 self(强引用),形成循环
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) 被释放")
}
}
// 循环引用导致实例无法释放
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello")
print(paragraph!.asHTML()) // 调用闭包,捕获 self
paragraph = nil // 无输出(未调用 deinit,内存泄漏)
解决闭包循环引用:捕获列表
[weak self]:闭包捕获self为弱引用(可选类型),适用于self可能为nil的场景(需解包)。[unowned self]:闭包捕获self为无主引用(非可选类型),适用于self永不为nil的场景(无需解包)。
class HTMLElement {
let name: String
let text: String?
// 用 [weak self] 打破循环(self 可能为 nil)
lazy var asHTML: () -> String = { [weak self] in
guard let self = self else { // 解包弱引用 self
return "实例已释放"
}
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) 被释放")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello")
print(paragraph!.asHTML()) // 输出:<p>hello</p>
paragraph = nil // 输出:p 被释放(循环已打破)
1.16 类型判断与向下转型
Swift 提供类型检查和类型转换能力,用于处理“不确定实例具体类型”的场景(如多态数组)。
1. 类型判断(is 关键字)
is 用于判断实例是否属于某个类型(或其子类)、是否遵循某个协议,返回 Bool 值。
class Subjects {
var physics: String
init(physics: String) {
self.physics = physics
}
}
class Chemistry: Subjects { // 子类
var equations: String
init(physics: String, equations: String) {
self.equations = equations
super.init(physics: physics)
}
}
class Maths: Subjects { // 子类
var formulae: String
init(physics: String, formulae: String) {
self.formulae = formulae
super.init(physics: physics)
}
}
// 多态数组:存储父类类型,实际是子类实例
let subjectArray: [Subjects] = [
Chemistry(physics: "固体物理", equations: "赫兹"),
Maths(physics: "流体力学", formulae: "千兆赫"),
Chemistry(physics: "热物理", equations: "分贝")
]
// 用 is 判断类型
var chemCount = 0
var mathsCount = 0
for item in subjectArray {
if item is Chemistry {
chemCount += 1
} else if item is Maths {
mathsCount += 1
}
}
print("化学类实例数:\(chemCount),数学类实例数:\(mathsCount)") // 输出:2,1
2. 向下转型(as? 与 as!)
向下转型是“将父类类型实例转为子类类型”的操作,需用 as? 或 as!:
as?:可选转型,成功返回子类实例(可选类型),失败返回nil(安全,推荐)。as!:强制转型,成功返回子类实例,失败触发运行时崩溃(仅确定类型时使用)。
// 用 as? 向下转型(安全)
for item in subjectArray {
if let chemItem = item as? Chemistry {
// 转型成功:item 是 Chemistry 实例
print("化学:\(chemItem.physics),公式:\(chemItem.equations)")
} else if let mathsItem = item as? Maths {
// 转型成功:item 是 Maths 实例
print("数学:\(mathsItem.physics),公式:\(mathsItem.formulae)")
}
}
// 输出:
// 化学:固体物理,公式:赫兹
// 数学:流体力学,公式:千兆赫
// 化学:热物理,公式:分贝
// 用 as! 强制转型(风险)
let firstItem = subjectArray[0]
let firstChem = firstItem as! Chemistry // 确定是 Chemistry,强制转型成功
print(firstChem.equations) // 输出:赫兹
// let firstMath = firstItem as! Maths // 错误:类型不匹配,崩溃
1.17 Any 与 AnyObject
Swift 提供两种“万能类型”,用于存储任意类型的实例:
| 类型 | 适用场景 | 限制 |
|---|---|---|
Any |
存储任何类型(值类型、引用类型、函数) | 无 |
AnyObject |
存储引用类型(仅类实例) | 不能存储值类型(如 Int、String) |
1. Any 类型示例
// Any 数组:可存储 Int、String、类实例等
var anyArray: [Any] = []
anyArray.append(10) // Int(值类型)
anyArray.append("Swift") // String(值类型)
anyArray.append(Chemistry(physics: "量子物理", equations: "薛定谔方程")) // 类实例(引用类型)
anyArray.append({ (x: Int) -> Int in x * 2 }) // 函数类型
// 遍历 Any 数组(需转型才能使用具体类型特性)
for item in anyArray {
switch item {
case let intValue as Int:
print("整数:\(intValue)")
case let strValue as String:
print("字符串:\(strValue)")
case let chemValue as Chemistry:
print("化学实例:\(chemValue.physics)")
case let funcValue as (Int) -> Int:
print("函数调用结果:\(funcValue(5))") // 调用函数
default:
print("未知类型")
}
}
// 输出:
// 整数:10
// 字符串:Swift
// 化学实例:量子物理
// 函数调用结果:10
2. AnyObject 类型示例
// AnyObject 数组:仅可存储类实例
var objectArray: [AnyObject] = []
objectArray.append(Chemistry(physics: "有机化学", equations: "化学键") as AnyObject)
objectArray.append(Maths(physics: "微积分", formulae: "导数公式") as AnyObject)
// 遍历 AnyObject 数组(需转型)
for item in objectArray {
if let chem = item as? Chemistry {
print("化学:\(chem.equations)")
} else if let maths = item as? Maths {
print("数学:\(maths.formulae)")
}
}
// 输出:
// 化学:化学键
// 数学:导数公式
// objectArray.append(10) // 错误:Int 是值类型,不能存入 AnyObject 数组
1.18 扩展(Extension)
扩展是“在不修改原类型源码的前提下,为类型添加新功能”的机制,支持为类、结构体、枚举、协议添加特性(计算属性、方法、下标等)。
1. 扩展计算属性
扩展可添加计算属性(存储属性不可添加,避免破坏原类型内存布局)。
// 为 Int 扩展计算属性(添加“加减乘除”快捷计算)
extension Int {
var add100: Int { return self + 100 }
var sub10: Int { return self - 10 }
var mul10: Int { return self * 10 }
var div5: Int { return self / 5 }
}
let num = 20
print(num.add100) // 输出:120
print(num.sub10) // 输出:10
print(num.mul10) // 输出:200
print(num.div5) // 输出:4
2. 扩展方法
扩展可添加实例方法和类型方法,结构体/枚举的方法若修改 self,需加 mutating。
// 为 Int 扩展实例方法(打印指定次数的内容)
extension Int {
func printTimes(_ content: String) {
for _ in 0..<self {
print(content)
}
}
// 结构体方法修改 self,需加 mutating
mutating func square() {
self = self * self
}
}
3.printTimes("Hello") // 输出 3 次 "Hello"
var num2 = 5
num2.square()
print(num2) // 输出:25
// 为 String 扩展类型方法(判断是否为邮箱格式,简化版)
extension String {
static func isEmail(_ str: String) -> Bool {
return str.contains("@") && str.contains(".")
}
}
print(String.isEmail("test@163.com")) // 输出:true
print(String.isEmail("test163.com")) // 输出:false
3. 扩展初始化器
扩展可为类型添加便捷初始化器(不能添加指定初始化器,避免覆盖原初始化器),类的扩展初始化器需调用原类型的指定初始化器。
struct Size {
var width: Double, height: Double
// 原指定初始化器
init(width: Double, height: Double) {
self.width = width
self.height = height
}
}
// 为 Size 扩展便捷初始化器(正方形,宽高相等)
extension Size {
init(sideLength: Double) {
self.init(width: sideLength, height: sideLength) // 调用原指定初始化器
}
}
let square = Size(sideLength: 10)
print("正方形宽:\(square.width),高:\(square.height)") // 输出:10.0,10.0
4. 扩展下标
扩展可为类型添加自定义下标,增强访问灵活性。
// 为 Int 扩展下标(获取数字的第 n 位,从右数,0 开始)
extension Int {
subscript(digitIndex: Int) -> Int {
var power = 1
for _ in 0..<digitIndex {
power *= 10
}
return (self / power) % 10
}
}
let number = 7869
print(number[0]) // 输出:9(个位)
print(number[1]) // 输出:6(十位)
print(number[2]) // 输出:8(百位)
print(number[3]) // 输出:7(千位)
5. 扩展嵌套类型
扩展可为类型添加嵌套类型,用于封装与原类型相关的逻辑。
// 为 Int 扩展嵌套枚举(表示数字类型)
extension Int {
enum NumberType {
case positive, negative, zero
}
// 扩展计算属性,返回数字类型
var type: NumberType {
switch self {
case 0: return .zero
case let x where x > 0: return .positive
default: return .negative
}
}
}
let num3 = 10
let num4 = -5
print(num3.type) // 输出:positive
print(num4.type) // 输出:negative
// 使用嵌套枚举判断类型
if num3.type == .positive {
print("\(num3) 是正数")
}
6. 扩展协议
扩展可给协议添加默认实现,让遵循协议的类型无需手动实现(仅未实现该方法时生效)。
// 定义协议
protocol Greetable {
func greet() -> String
}
// 扩展协议,添加默认实现
extension Greetable {
func greet() -> String {
return "Hello, 默认问候!"
}
// 扩展可添加协议未声明的方法(仅遵循者可调用)
func greetWithName(_ name: String) -> String {
return "Hello, \(name)!"
}
}
// 类遵循协议,未实现 greet()(使用默认实现)
class Person: Greetable {}
let person = Person()
print(person.greet()) // 输出:Hello, 默认问候!
print(person.greetWithName("小明")) // 输出:Hello, 小明!
// 类遵循协议,重写 greet()(覆盖默认实现)
class Student: Greetable {
func greet() -> String {
return "Hello, 我是学生!"
}
}
let student = Student()
print(student.greet()) // 输出:Hello, 我是学生!
1.19 协议(Protocol)
协议是“定义接口规范”的蓝图,之前介绍了基本用法,此处补充高级特性。
1. 协议继承
协议可继承一个或多个其他协议,遵循该协议的类型需实现所有父协议的要求。
// 父协议 1
protocol Runnable {
func run()
}
// 父协议 2
protocol Eatable {
func eat()
}
// 子协议:继承两个父协议
protocol AnimalProtocol: Runnable, Eatable {
var name: String { get }
}
// 类遵循子协议,需实现所有父协议和子协议的要求
class Dog: AnimalProtocol {
var name: String = "小狗"
func run() {
print("\(name) 在跑步")
}
func eat() {
print("\(name) 在吃骨头")
}
}
let dog = Dog()
dog.run() // 输出:小狗 在跑步
dog.eat() // 输出:小狗 在吃骨头
2. 协议合成(Protocol Composition)
用 & 组合多个协议,表示“同时遵循这些协议的类型”,适用于函数参数、变量声明等场景(无需定义新协议)。
// 协议 1
protocol Named {
var name: String { get }
}
// 协议 2
protocol Aged {
var age: Int { get }
}
// 结构体同时遵循两个协议
struct Person: Named, Aged {
var name: String
var age: Int
}
// 函数参数:协议合成(同时遵循 Named 和 Aged)
func printPersonInfo(_ person: Named & Aged) {
print("姓名:\(person.name),年龄:\(person.age)")
}
let person = Person(name: "小红", age: 20)
printPersonInfo(person) // 输出:姓名:小红,年龄:20
3. 类专属协议
用 AnyObject 约束协议,使其**只能被类遵循(**结构体、枚举无法遵循),避免值类型的引用问题。
// 类专属协议(用 AnyObject 约束)
protocol ClassOnlyProtocol: AnyObject {
func doSomething()
}
// 类可遵循
class MyClass: ClassOnlyProtocol {
func doSomething() {
print("类实现协议方法")
}
}
// 结构体无法遵循(编译错误)
// struct MyStruct: ClassOnlyProtocol {
// func doSomething() {}
// }
4. 协议关联类型(associatedtype)
协议中用 associatedtype 声明“占位类型”,表示“协议需要的类型由遵循者指定”,是泛型协议的核心。
// 定义泛型协议(用 associatedtype 占位)
protocol Container {
associatedtype ItemType // 占位类型:由遵循者指定
mutating func append(_ item: ItemType) // 用占位类型定义参数
var count: Int { get }
subscript(i: Int) -> ItemType { get } // 用占位类型定义返回值
}
// 结构体遵循协议,指定 ItemType 为 String
struct StringStack: Container {
typealias ItemType = String // 显式指定(可省略,编译器自动推断)
private var items = [String]()
mutating func append(_ item: String) {
items.append(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> String {
return items[i]
}
}
// 结构体遵循协议,指定 ItemType 为 Int(编译器自动推断)
struct IntStack: Container {
private var items = [Int]()
mutating func append(_ item: Int) { // 编译器推断 ItemType = Int
items.append(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}
// 使用泛型协议
var strStack = StringStack()
strStack.append("Swift")
strStack.append("协议")
print(strStack[0]) // 输出:Swift
var intStack = IntStack()
intStack.append(10)
intStack.append(20)
print(intStack.count) // 输出:2
1.20 泛型(Generics)
泛型是“编写通用代码,适配多种类型”的机制,之前介绍了基础用法,此处补充高级特性。
1. 类型约束(Type Constraints)
泛型默认支持任意类型,类型约束可限制泛型参数必须是“某个类的子类”或“遵循某个协议”,确保代码可调用特定类型的特性。
(1)约束为类的子类
class Animal {
func eat() {
print("动物进食")
}
}
class Dog: Animal {
override func eat() {
print("狗吃骨头")
}
}
class Cat: Animal {
override func eat() {
print("猫吃鱼")
}
}
// 泛型函数:约束 T 必须是 Animal 的子类
func feedAnimal<T: Animal>(_ animal: T) {
animal.eat() // 可安全调用 Animal 的 eat() 方法
}
feedAnimal(Dog()) // 输出:狗吃骨头
feedAnimal(Cat()) // 输出:猫吃鱼
(2)约束为遵循协议
// 泛型函数:约束 T 必须遵循 Equatable 协议(支持 == 比较)
func isEqual<T: Equatable>(_ a: T, _ b: T) -> Bool {
return a == b
}
print(isEqual(10, 10)) // 输出:true(Int 遵循 Equatable)
print(isEqual("Swift", "swift")) // 输出:false(String 遵循 Equatable)
2. where 子句(高级约束)
where 子句可给泛型添加 额外约束(如“两个泛型参数类型相同”“泛型参数遵循某个协议”),比基础类型约束更灵活。
(1)泛型函数中的 where
// 泛型协议(之前定义的 Container)
protocol Container {
associatedtype ItemType
mutating func append(_ item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
// 泛型函数:比较两个 Container 的元素是否完全匹配
func allItemsMatch<C1: Container, C2: Container>(
_ container1: C1,
_ container2: C2
) -> Bool where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
// 约束 1:C1 和 C2 的 ItemType 必须相同
// 约束 2:ItemType 必须遵循 Equatable(支持 == 比较)
if container1.count != container2.count {
return false
}
for i in 0..<container1.count {
if container1[i] != container2[i] { // 可安全比较
return false
}
}
return true
}
// 测试:两个 StringStack 比较
var stack1 = StringStack()
stack1.append("Swift")
stack1.append("泛型")
var stack2 = StringStack()
stack2.append("Swift")
stack2.append("泛型")
print(allItemsMatch(stack1, stack2)) // 输出:true
// 测试:StringStack 和 Array(Array 扩展后遵循 Container)
extension Array: Container {} // Array 遵循 Container,ItemType 为元素类型
var array = ["Swift", "泛型"]
print(allItemsMatch(stack1, array)) // 输出:true
(2)泛型类型中的 where
// 泛型结构体
struct Pair<T, U> {
let first: T
let second: U
}
// 扩展泛型结构体,添加方法(仅当 T 和 U 都遵循 Equatable 时生效)
extension Pair where T: Equatable, U: Equatable {
func isBothEqual(to other: Pair<T, U>) -> Bool {
return self.first == other.first && self.second == other.second
}
}
// T=Int、U=String(均遵循 Equatable),可调用 isBothEqual
let pair1 = Pair(first: 10, second: "A")
let pair2 = Pair(first: 10, second: "A")
print(pair1.isBothEqual(to: pair2)) // 输出:true
// T=Any、U=Any(不遵循 Equatable),无 isBothEqual 方法
let pair3 = Pair(first: 10, second: [1, 2, 3])
// pair3.isBothEqual(to: ...) // 错误:无此方法
1.21 访问控制(Access Control)
访问控制用于“限制代码的可见范围”,确保封装性,Swift 提供 4 个访问级别(从高到低):
| 访问级别 | 可见范围 | 适用场景 |
|---|---|---|
public |
模块内外均可访问 | 框架对外暴露的接口 |
internal |
仅模块内可访问(默认级别) | 模块内部使用的代码 |
fileprivate |
仅当前源文件内可访问 | 文件内私有逻辑 |
private |
仅当前类型内可访问(类/结构体/枚举内部) | 类型内部私有属性/方法 |
1. 核心规则
(1)子类访问级别 ≤ 父类访问级别
子类的访问级别不能高于父类,避免“父类不可见但子类可见”的矛盾。
public class SuperClass { // 父类:public
fileprivate func show() { // 父类方法:fileprivate
print("父类方法")
}
}
// 子类:internal(≤ public,合法)
internal class SubClass: SuperClass {
// 重写方法:fileprivate(≤ 父类方法的 fileprivate,合法)
override fileprivate func show() {
print("子类方法")
}
}
// 子类:public(= 父类 public,合法)
public class SubClass2: SuperClass {
// 重写方法:public(> 父类方法的 fileprivate,错误)
// override public func show() {}
}
(2)协议成员访问级别 ≥ 协议访问级别
协议的访问级别决定其成员的最低访问级别(public 协议的成员至少是 public),确保协议接口可访问。
public protocol PublicProtocol {
func doSomething() // 成员默认是 public(必须实现为 public)
}
// 类遵循 public 协议,方法必须是 public
public class MyClass: PublicProtocol {
public func doSomething() { // public,合法
print("实现协议方法")
}
}
// 类遵循 public 协议,方法是 internal(< public,错误)
// class MyClass2: PublicProtocol {
// internal func doSomething() {}
// }
(3)类型别名访问级别 ≤ 原类型访问级别
类型别名的访问级别不能高于原类型,避免“原类型不可见但别名可见”。
fileprivate class PrivateClass {} // 原类型:fileprivate
// 别名:fileprivate(≤ 原类型,合法)
fileprivate typealias PrivateAlias = PrivateClass
// 别名:internal(> 原类型,错误)
// internal typealias InternalAlias = PrivateClass
2. 实战示例
// 模块内使用(internal,默认)
internal struct User {
private var password: String // 仅 User 内部可访问
internal var name: String // 模块内可访问
init(name: String, password: String) {
self.name = name
self.password = password
}
// 内部方法:可访问 private 属性
internal func verifyPassword(_ input: String) -> Bool {
return self.password == input
}
}
// 模块内使用
let user = User(name: "小明", password: "123456")
print(user.name) // 输出:小明(internal 可见)
// print(user.password) // 错误:private 不可访问
print(user.verifyPassword("123456")) // 输出:true(internal 方法可见)
1.22 类型别名(Type Alias)
类型别名是“给已有类型起新名字”,除了增强可读性,还可简化复杂类型(如闭包类型、泛型类型)。
1. 简化复杂类型
// 复杂闭包类型
typealias CalculateFunc = (Int, Int) -> Int
// 用类型别名简化函数参数
func calculate(_ a: Int, _ b: Int, _ funcType: CalculateFunc) -> Int {
return funcType(a, b)
}
// 定义具体闭包
let add: CalculateFunc = { $0 + $1 }
let multiply: CalculateFunc = { $0 * $1 }
print(calculate(10, 5, add)) // 输出:15
print(calculate(10, 5, multiply)) // 输出:50
// 简化泛型类型
typealias StringDictionary = [String: Any] // 字符串键的字典
let userDict: StringDictionary = ["name": "小红", "age": 20]
typealias IntStack = Stack<Int> // 泛型栈的具体类型(Stack 是之前定义的泛型结构体)
var stack: IntStack = IntStack()
stack.append(10)
2. 关联协议类型
类型别名可用于明确协议的关联类型,增强代码可读性。
protocol Container {
associatedtype ItemType
mutating func append(_ item: ItemType)
var count: Int { get }
}
// 扩展 Array 遵循 Container,用类型别名明确关联类型
extension Array: Container {
typealias ItemType = Element // Element 是 Array 的泛型参数
}
let arr: [String] = ["A", "B"]
print(arr.count) // 输出:2(Container 协议的 count 属性)
更多推荐

所有评论(0)