Swift面向对象编程基础
类与对象基础
在Swift的面向对象编程中,类(class)是构建程序的基本单元,它定义了一种新的数据类型,包含属性(properties)和方法(methods)。属性用于存储数据,而方法则用于定义可对数据执行的操作。
类的定义
定义一个类使用class
关键字,后面跟着类名,类名通常采用大写字母开头的驼峰命名法(CamelCase)。例如,定义一个简单的Person
类:
class Person {
// 属性定义
var name: String
var age: Int
// 构造函数
init(name: String, age: Int) {
self.name = name
self.age = age
}
// 方法定义
func introduce() {
print("Hello, my name is \(name) and I'm \(age) years old.")
}
}
在上述代码中,Person
类有两个属性name
和age
,分别表示人的姓名和年龄。init
函数是构造函数,用于初始化类的实例。introduce
方法用于打印个人信息。
对象的创建与使用
创建类的实例(对象)时,使用类名加上括号,并传入构造函数所需的参数。例如:
let john = Person(name: "John", age: 30)
john.introduce()
上述代码创建了一个Person
类的实例john
,并调用其introduce
方法,输出Hello, my name is John and I'm 30 years old.
。
类的属性
存储属性
存储属性是类中用于存储值的变量或常量。在前面的Person
类中,name
和age
就是存储属性。存储属性可以是变量属性(使用var
关键字定义),也可以是常量属性(使用const
关键字定义)。常量属性一旦初始化后就不能再被修改。
计算属性
计算属性不直接存储值,而是通过一个getter
方法来计算并返回一个值,还可以通过setter
方法来设置新的值(对于只读计算属性,只有getter
方法)。例如,定义一个Rectangle
类,包含宽度和高度属性,以及一个计算面积的计算属性:
class Rectangle {
var width: Double
var height: Double
init(width: Double, height: Double) {
self.width = width
self.height = height
}
var area: Double {
get {
return width * height
}
}
}
let rect = Rectangle(width: 5.0, height: 3.0)
print("The area of the rectangle is \(rect.area)")
在上述代码中,area
是一个只读计算属性,通过getter
方法返回矩形的面积。
属性观察器
属性观察器可以在属性值发生变化时执行特定的代码。有两种属性观察器:willSet
和didSet
。willSet
在属性值即将被设置时调用,didSet
在属性值已经被设置后调用。例如,在Person
类中添加一个属性观察器来监测age
属性的变化:
class Person {
var name: String
var age: Int {
willSet(newAge) {
print("The age is about to change to \(newAge)")
}
didSet {
if age != oldValue {
print("The age has changed from \(oldValue) to \(age)")
}
}
}
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
let tom = Person(name: "Tom", age: 25)
tom.age = 26
上述代码中,当tom
的age
属性值发生变化时,willSet
和didSet
中的代码会被执行,打印相应的信息。
类的方法
实例方法
实例方法是属于类实例的方法,它可以访问和修改实例的属性。在前面的Person
类中,introduce
方法就是一个实例方法。实例方法可以带有参数,也可以返回值。例如,在Rectangle
类中添加一个方法来判断矩形是否为正方形:
class Rectangle {
var width: Double
var height: Double
init(width: Double, height: Double) {
self.width = width
self.height = height
}
func isSquare() -> Bool {
return width == height
}
}
let squareRect = Rectangle(width: 4.0, height: 4.0)
let nonSquareRect = Rectangle(width: 5.0, height: 3.0)
print(squareRect.isSquare()) // 输出 true
print(nonSquareRect.isSquare()) // 输出 false
在上述代码中,isSquare
方法是一个实例方法,它返回一个布尔值,表示矩形是否为正方形。
类型方法
类型方法是属于类本身的方法,而不是属于类的实例。在方法定义前加上static
关键字来定义类型方法。例如,定义一个MathUtils
类,包含一个类型方法来计算两个数的和:
class MathUtils {
static func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
}
let result = MathUtils.add(3, 5)
print("The sum is \(result)")
在上述代码中,add
方法是一个类型方法,通过类名直接调用,而不需要创建类的实例。
继承
继承是面向对象编程的重要特性之一,它允许一个类从另一个类继承属性和方法。被继承的类称为父类(superclass),继承的类称为子类(subclass)。子类可以在继承的基础上添加新的属性和方法,或者重写父类的方法。
继承的定义
定义一个子类时,在类名后面加上冒号(:
),然后跟上父类的名称。例如,定义一个Student
类继承自Person
类:
class Student: Person {
var studentID: String
init(name: String, age: Int, studentID: String) {
self.studentID = studentID
super.init(name: name, age: age)
}
override func introduce() {
print("Hello, I'm \(name), a student with ID \(studentID), and I'm \(age) years old.")
}
}
在上述代码中,Student
类继承自Person
类,添加了一个新的属性studentID
,并重写了introduce
方法以包含学生ID信息。在init
构造函数中,通过super.init
调用父类的构造函数来初始化继承的属性。
重写方法
当子类需要提供与父类不同的实现时,可以重写父类的方法。在重写方法前需要加上override
关键字,以表明这是一个重写的方法。如上面Student
类中重写的introduce
方法。如果子类中没有使用override
关键字而定义了与父类方法同名的方法,编译器会报错。
访问父类成员
在子类中,可以通过super
关键字访问父类的属性和方法。例如,在Student
类的introduce
方法中,如果想要保留父类introduce
方法的部分功能,可以这样做:
class Student: Person {
var studentID: String
init(name: String, age: Int, studentID: String) {
self.studentID = studentID
super.init(name: name, age: age)
}
override func introduce() {
super.introduce()
print("My student ID is \(studentID)")
}
}
let alice = Student(name: "Alice", age: 20, studentID: "S12345")
alice.introduce()
上述代码中,super.introduce()
调用了父类Person
的introduce
方法,然后再打印学生ID信息。
访问控制
访问控制用于限制代码中不同部分对类、属性、方法等的访问权限。Swift提供了几种访问控制级别:public
、internal
、fileprivate
和private
。
访问控制级别
public
:最高访问级别,允许在任何源文件中访问。通常用于暴露给其他模块使用的类、属性和方法。internal
:默认访问级别,允许在定义它们的模块内部访问。如果一个应用程序只有一个模块,internal
和public
在效果上类似,但internal
的成员不能被其他模块访问。fileprivate
:限制访问仅限于定义它们的源文件内部。同一模块中的其他源文件无法访问fileprivate
成员。private
:最低访问级别,限制访问仅限于定义它们的作用域内。例如,在类中定义的private
属性或方法,只能在该类的内部访问。
设置访问控制
在定义类、属性或方法时,可以在前面加上相应的访问控制关键字来设置访问级别。例如:
public class PublicClass {
public var publicProperty: String
internal var internalProperty: String
fileprivate var fileprivateProperty: String
private var privateProperty: String
public init(publicProp: String, internalProp: String, fileprivateProp: String, privateProp: String) {
self.publicProperty = publicProp
self.internalProperty = internalProp
self.fileprivateProperty = fileprivateProp
self.privateProperty = privateProp
}
public func publicMethod() {
print("This is a public method.")
}
internal func internalMethod() {
print("This is an internal method.")
}
fileprivate func fileprivateMethod() {
print("This is a file - private method.")
}
private func privateMethod() {
print("This is a private method.")
}
}
在上述代码中,PublicClass
类及其成员分别设置了不同的访问控制级别。
构造过程与析构过程
构造函数
构造函数是类实例化时调用的特殊方法,用于初始化实例的属性。在Swift中,构造函数使用init
关键字定义。一个类可以有多个构造函数,称为构造函数重载,只要它们的参数列表不同。例如,在Rectangle
类中添加一个默认构造函数:
class Rectangle {
var width: Double
var height: Double
// 带参数的构造函数
init(width: Double, height: Double) {
self.width = width
self.height = height
}
// 默认构造函数
init() {
self.width = 0.0
self.height = 0.0
}
}
let rect1 = Rectangle(width: 5.0, height: 3.0)
let rect2 = Rectangle()
在上述代码中,Rectangle
类有两个构造函数,一个接受宽度和高度参数,另一个是默认构造函数,将宽度和高度初始化为0。
可失败构造函数
可失败构造函数是一种特殊的构造函数,它可能会因为某些条件不满足而初始化失败。可失败构造函数使用init?
关键字定义。例如,定义一个Circle
类,其构造函数在半径为负数时初始化失败:
class Circle {
var radius: Double
init?(radius: Double) {
if radius < 0 {
return nil
}
self.radius = radius
}
}
let validCircle = Circle(radius: 5.0)
let invalidCircle = Circle(radius: -2.0)
if let circle = validCircle {
print("The radius of the circle is \(circle.radius)")
}
if invalidCircle == nil {
print("Invalid circle creation.")
}
在上述代码中,当半径为负数时,Circle
的构造函数返回nil
,表示初始化失败。
析构函数
析构函数是在类实例被销毁之前调用的特殊方法,用于释放实例所占用的资源。在Swift中,析构函数使用deinit
关键字定义。例如,定义一个FileManager
类,在析构函数中关闭文件:
class FileManager {
var file: FileHandle?
init(filePath: String) {
if let file = FileHandle(forWritingAtPath: filePath) {
self.file = file
}
}
deinit {
file?.closeFile()
file = nil
}
}
let fileMgr = FileManager(filePath: "test.txt")
// 当fileMgr超出作用域被销毁时,deinit函数会被调用
在上述代码中,FileManager
类的析构函数关闭打开的文件,并将file
属性设置为nil
。
类的扩展
扩展(extension)允许在不修改原始类定义的情况下,为类添加新的属性、方法、构造函数等。扩展使用extension
关键字定义。
扩展属性
可以为类扩展计算属性,但不能扩展存储属性。例如,为Int
类型扩展一个计算属性,用于判断该整数是否为偶数:
extension Int {
var isEven: Bool {
return self % 2 == 0
}
}
let number = 4
print(number.isEven) // 输出 true
在上述代码中,为Int
类型扩展了一个isEven
计算属性,通过这个属性可以方便地判断一个整数是否为偶数。
扩展方法
可以为类扩展实例方法和类型方法。例如,为String
类型扩展一个方法,用于重复字符串指定次数:
extension String {
func repeat(_ times: Int) -> String {
var result = ""
for _ in 0..<times {
result += self
}
return result
}
}
let str = "Hello"
let repeatedStr = str.repeat(3)
print(repeatedStr) // 输出 HelloHelloHello
在上述代码中,为String
类型扩展了一个repeat
方法,用于重复字符串。
扩展构造函数
可以为类扩展构造函数,但需要注意,扩展的构造函数不能重写类原有的构造函数。例如,为Rectangle
类扩展一个构造函数,从字符串中解析宽度和高度:
extension Rectangle {
init?(fromString: String) {
let components = fromString.components(separatedBy: "x")
if components.count != 2 {
return nil
}
guard let width = Double(components[0]), let height = Double(components[1]) else {
return nil
}
self.init(width: width, height: height)
}
}
let rectFromString = Rectangle(fromString: "5.0x3.0")
if let rect = rectFromString {
print("Width: \(rect.width), Height: \(rect.height)")
}
在上述代码中,为Rectangle
类扩展了一个从字符串解析宽度和高度的构造函数,当解析失败时返回nil
。
协议
协议(protocol)定义了方法、属性等的蓝图,一个类可以遵循(conform to)协议,并实现协议中定义的要求。协议使用protocol
关键字定义。
协议定义
例如,定义一个Drawable
协议,要求遵循该协议的类必须实现draw
方法:
protocol Drawable {
func draw()
}
上述Drawable
协议定义了一个draw
方法,但没有实现,遵循该协议的类需要提供具体的实现。
类遵循协议
让Rectangle
类遵循Drawable
协议,并实现draw
方法:
class Rectangle: Drawable {
var width: Double
var height: Double
init(width: Double, height: Double) {
self.width = width
self.height = height
}
func draw() {
print("Drawing a rectangle with width \(width) and height \(height)")
}
}
let rect = Rectangle(width: 4.0, height: 3.0)
rect.draw()
在上述代码中,Rectangle
类遵循了Drawable
协议,并实现了draw
方法。
协议作为类型
协议可以作为一种类型来使用。例如,可以定义一个函数,接受一个遵循Drawable
协议的对象:
func drawShape(shape: Drawable) {
shape.draw()
}
let rectForDrawing = Rectangle(width: 5.0, height: 2.0)
drawShape(shape: rectForDrawing)
在上述代码中,drawShape
函数接受一个遵循Drawable
协议的对象,并调用其draw
方法,这体现了多态性,不同的类只要遵循Drawable
协议,都可以作为参数传递给该函数。
协议继承
协议可以继承其他协议,从而扩展其功能。例如,定义一个FillableDrawable
协议,继承自Drawable
协议,并添加一个fillColor
属性和fill
方法:
protocol FillableDrawable: Drawable {
var fillColor: String { get set }
func fill()
}
class FilledRectangle: Rectangle, FillableDrawable {
var fillColor: String
init(width: Double, height: Double, fillColor: String) {
self.fillColor = fillColor
super.init(width: width, height: height)
}
func fill() {
print("Filling the rectangle with color \(fillColor)")
}
}
let filledRect = FilledRectangle(width: 3.0, height: 2.0, fillColor: "Red")
filledRect.draw()
filledRect.fill()
在上述代码中,FilledRectangle
类遵循了FillableDrawable
协议,实现了fillColor
属性和fill
方法,同时继承自Rectangle
类并实现了draw
方法。
通过以上内容,我们全面地介绍了Swift面向对象编程的基础,包括类与对象、属性、方法、继承、访问控制、构造与析构、扩展以及协议等方面,这些知识为进一步深入学习Swift编程和开发复杂应用奠定了坚实的基础。在实际开发中,灵活运用这些面向对象的特性,可以编写出结构清晰、可维护性强且功能丰富的程序。继续深入学习和实践,你将能够充分发挥Swift语言在面向对象编程方面的强大能力。