Skip to content
返回

Swift 中级篇

目录

Struct 结构体

建立 Struct

与类(Class)类似

struct Person {
    var name: String
    var age: Int

    func greet() {
        print("Hello, my name is \(name) and I am \(age) years old.")
    }
}

let person = Person(name: "Alice", age: 25)
person.greet()
// 输出: Hello, my name is Alice and I am 25 years old.

结构体与类的比较

**Struct**(结构体) 是一种 值类型,用来封装相关的数据和功能。与类(Class)类似,结构体可以定义属性、方法和初始化器,但它与类最大的不同在于它是 值类型,而Class类是 引用类型

特性Struct(结构体)Class(类)
类型值类型引用类型
内存管理不使用引用计数使用引用计数(ARC)
拷贝行为赋值或传递时会被复制赋值或传递时共享同一个实例
继承不支持支持
可变性let 定义的实例不可变,属性也不可变let 定义的实例,属性仍可变

值类型的特点

struct Point {
    var x: Int
    var y: Int
}

var point1 = Point(x: 1, y: 2)
var point2 = point1  // 创建一个副本

point2.x = 10

print(point1.x)  // 输出: 1(原始值未改变)
print(point2.x)  // 输出: 10

func & mutating

struct Point {
    var x: Int
    var y: Int

    // 自定义初始化器
    init(_ x: Int, y: Int) {
        self.x = x
        self.y = y
    }

    // 析构器用于在实例被销毁之前执行清理工作
    deinit {
        print("\(x) is being deinitialized.")
    }

    // 实例方法:打印 x 和 y 的值
    func log() {
        print("x: \(x), y: \(y)")
    }

    // 在方法中,属性是不可变的,除非方法被标记为 mutating。
    mutating func move(dx: Int, dy: Int) {
        x += dx
        y += dy
    }
}

var point = Point(x: 1, y: 2)
point.log()    // 输出: x: 1, y: 2

point.move(dx: 3, dy: 4)  // 移动点
point.log()    // 输出: x: 4, y: 6

static

struct Point {
		// static let zero 定义了一个静态常量 zero,它属于 Point 类型,不是任何 Point 实例的。
		static let zero: Int = 0
    var x: Int
    var y: Int

    // static 方法,属于类型,不能访问实例属性 x 和 y
    // 静态方法,可以直接通过类型名 Point.square 调用
    static func square(_ number: Int) -> Int {
        print(zero)  // 正确,static 方法可以访问 static 属性

        // 在 static 方法中,self 指代的是类型,而不是实例。
        print(self.zero)  // 使用 self 访问静态属性

        return number * number
    }
}

print(Point.zero)  // 调用 static 属性,输出: 0

let result = Point.square(2)  // 调用 static 方法
print(result)  // 输出: 4

init & deinit

结构体没有 deinit,因为它不需要处理复杂的内存管理逻辑(如强引用循环)。

结构体是值类型,不通过引用计数管理生命周期。当结构体超出作用域时,Swift 会直接释放其内存,而无需像引用类型(类)那样调用析构器来手动清理资源。

class Point {
    var x: Int
    var y: Int

    init(_ x: Int, y: Int) {
        self.x = x
        self.y = y
    }

    deinit {
        print("Point (\(x), \(y)) is being deinitialized.")
    }
}

// 测试类的析构器
var point: Point? = Point(1, y: 2)
point = nil  // 输出: Point (1, 2) is being deinitialized.

**private** 访问控制

**private(set)****get** 是属性访问控制的一部分,它们用来控制属性的访问权限,尤其是在类、结构体或枚举中。你可以指定属性的 **getter****setter** 方法的访问级别,以实现更细粒度的控制。

struct Person {
		private var age = 0  // 私有属性,外部不可以读和写
    private init() {}    // 不需要 init 可以 private 掉
}

使用 private(set) 来保护类的某个属性不被外部直接修改,但允许外部读取该属性的值。

class Counter {
    private(set) var count = 0  // count 的 getter 是公开的,setter 是私有的

    func increment() {
        count += 1  // 可以在类内部修改
    }

    func reset() {
        count = 0  // 可以在类内部修改
    }
}

let counter = Counter()
print(counter.count)  // 输出: 0,外部可以读取值
counter.increment()
print(counter.count)  // 输出: 1,外部可以读取值
// counter.count = 10  // 错误:无法访问 setter,不能修改 count 的值

遵循 protocol 协议

protocol Greetable {
    var name: String { get }  // 属性要求
    func greet()              // 方法要求
}

struct Person: Greetable {
    var name: String          // 属性实现

    func greet() {            // 方法实现
        print("Hello, my name is \(name).")
    }
}

let person = Person(name: "Alice")
person.greet()
// 输出: Hello, my name is Alice.

print(person is Greetable)  // 输出: true

计算属性

struct Rectangle {
    var width: Double
    var height: Double

    // 计算属性:计算矩形的面积
    var area: Double {
        return width * height
    }

    // 计算属性:计算矩形的周长
    var perimeter: Double {
        return 2 * (width + height)
    }
}

let rect = Rectangle(width: 10, height: 5)
print("Area: \(rect.area)")       // 输出: Area: 50.0
print("Perimeter: \(rect.perimeter)") // 输出: Perimeter: 30.0

setter

struct Circle {
    var radius: Double

    // 计算属性:计算圆的面积
    var area: Double {
        get {
            return 3.14159 * radius * radius
        }
        set {
            radius = sqrt(newValue / 3.14159)  // 使用传入的新值来更新 radius
        }
    }
}

var circle = Circle(radius: 5)
print("Initial Area: \(circle.area)") // 输出: Initial Area: 78.53975

// 使用 setter 设置新值来改变 radius
circle.area = 50
print("New Radius: \(circle.radius)") // 输出: New Radius: 3.989422804014327
print("New Area: \(circle.area)")     // 输出: New Area: 50.0

只读

struct Person {
    var firstName: String
    var lastName: String

    // 只读计算属性:返回全名
    var fullName: String {
        return "\(firstName) \(lastName)"
    }
}

let person = Person(firstName: "John", lastName: "Doe")
print(person.fullName)  // 输出: John Doe

属性观察器

可以在计算属性中使用 **willSet****didSet** 来观察和响应属性值的变化,但对于计算属性本身,通常只会使用 **didSet** 来观察它们所依赖的其他属性变化。

struct Rectangle {
    var width: Double {
        willSet {
            print("Width will be set to \(newValue)")
        }
        didSet {
            print("Width changed from \(oldValue) to \(width)")
        }
    }
    var height: Double {
        willSet {
            print("Height will be set to \(newValue)")
        }
        didSet {
            print("Height changed from \(oldValue) to \(height)")
        }
    }

    // 计算属性:计算矩形的面积
    var area: Double {
        return width * height
    }
}

var rect = Rectangle(width: 10, height: 5)
rect.width = 20      // 输出: Width will be set to 20
                     // 输出: Width changed from 10.0 to 20.0
rect.height = 10     // 输出: Height will be set to 10
                     // 输出: Height changed from 5.0 to 10.0
print("Area: \(rect.area)")  // 输出: Area: 200.0

lazy

class MyClass {
    var firstName: String
    var lastName: String

    // 计算属性,实时反映最新的值
    var fullName3: String { "\(firstName) \(lastName)" }

    // 计算属性 首次捕获计算
    lazy var fullName2: String = "\(firstName) \(lastName)"

    // 闭包 首次捕获计算
    lazy var fullName: String = {
				print("get fullName ..")
        return "\(firstName) \(lastName)"
    }()

    init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
    }
}

let person = MyClass(firstName: "John", lastName: "Doe")
print("lazy ..")         // 输出: lazy ..
print(person.fullName)   // 输出: get fullName ..
                         // 输出: John Doe
print(person.fullName)   // 输出: John Doe
print(person.fullName2)  // 输出: John Doe
print(person.fullName3)  // 输出: John Doe

person.firstName = "Jane"
print(person.fullName)   // 输出: John Doe
print(person.fullName2)  // 输出: John Doe
print(person.fullName3)  // 输出: Jane Doe

简单的 lazy 属性

class DataFetcher {
    lazy var data: String = {
        // 模拟一些计算或数据加载
        print("Data is being loaded...")
        return "Fetched Data"
    }()

    func fetchData() -> String {
        return data
    }
}

let fetcher = DataFetcher()
print("Before accessing data.")
print(fetcher.fetchData())  // 第一次访问时加载数据
print(fetcher.fetchData())  // 后续访问直接返回已经加载的数据

计算闭包

class Database {
    lazy var connection: String = {
        // 模拟建立数据库连接的操作
        print("Connecting to the database...")
        return "Database Connection Established"
    }()
}

let db = Database()
print("Before connecting to database.")
print(db.connection)  // 第一次访问时建立

Property Wrapper 装饰器

@propertyWrapper
struct Uppercase {
    private var value: String

    init(wrappedValue: String) {
        self.value = wrappedValue
    }

    var wrappedValue: String {
        get { return value }
        set { value = newValue.uppercased() }
    }
}

struct Person {
    @Uppercase var name: String
}

var person = Person(name: "john doe")
print(person.name)  // 输出: john doe
person.name = "jane doe"
print(person.name)  // 输出: JANE DOE


上一篇
全新的个人网站
下一篇
Swift 基础篇