美文网首页Swift学习记录
7、Swift 中的内存管理

7、Swift 中的内存管理

作者: Fred丶Lee | 来源:发表于2023-04-20 19:46 被阅读0次

    Swift 是一种快速、安全、现代的编程语言,它具有自动引用计数(ARC)机制来管理内存。这个机制可以自动追踪和释放不再需要的内存,使得开发者不必手动处理内存分配和释放的问题。在这篇博客中,我们将介绍 Swift 中的内存管理机制,以及如何避免内存泄漏和野指针等问题。

    ARC 简介
    Swift 使用自动引用计数(ARC)机制来追踪并管理内存。每个类实例都有一个引用计数,当一个实例被引用时,它的引用计数将增加。当一个实例不再被引用时,它的引用计数将减少。当引用计数变为 0 时,实例的内存将被自动释放。

    例如,下面的代码展示了如何创建一个类并实例化它:

    class Person {
        var name: String
        
        init(name: String) {
            self.name = name
            print("\(name) is being initialized.")
        }
        
        deinit {
            print("\(name) is being deinitialized.")
        }
    }
    
    var person1: Person?
    var person2: Person?
    var person3: Person?
    
    person1 = Person(name: "Alice")  // 输出 "Alice is being initialized."
    person2 = person1
    person3 = person1
    
    person1 = nil
    person2 = nil
    
    // 输出 "Alice is being deinitialized."
    

    在上面的例子中,我们创建了一个 Person 类,它有一个字符串类型的属性 name。我们实例化了三个 Person 对象,然后将 person1 和 person2 的引用都指向了第一个对象。这时,person1 和 person2 的引用计数都变成了 2。当我们将 person1 和 person2 的引用都置为 nil 时,引用计数减少到 0,Person 对象的内存就会被自动释放。

    内存泄漏
    内存泄漏是一种常见的问题,指的是在程序运行时,一些对象无法被回收并释放,从而导致内存占用不断增加,最终导致程序崩溃。在 Swift 中,内存泄漏通常是由于循环引用造成的。

    循环引用指的是两个或多个对象之间互相持有对方的强引用,从而导致它们的引用计数永远不会变为 0,即使它们不再被使用。这时,内存就会一直被占用,从而导致内存泄漏。

    例如,下面的代码展示了一个循环引用的例子:

    class Person {
        var name: String
        var pet: Pet?
        
        init(name: String) {
            self.name = name
        print("\(name) is being initialized.")
    }
    
    deinit {
        print("\(name) is being deinitialized.")
    }
    
    class Pet {
    var name: String
    var owner: Person?
    
    init(name: String) {
        self.name = name
        print("\(name) is being initialized.")
    }
    
    deinit {
        print("\(name) is being deinitialized.")
    }
    

    var person: Person?
    var pet: Pet?

    person = Person(name: "Alice")
    pet = Pet(name: "Fluffy")

    person?.pet = pet
    pet?.owner = person

    person = nil
    pet = nil

    // 输出 "Alice is being deinitialized."
    // 输出 "Fluffy is being deinitialized."

    swift
    Copy code

    在上面的例子中,我们创建了一个 Person 类和一个 Pet 类,它们之间相互持有对方的强引用。当我们将 personpet 的引用都置为 nil 时,它们的引用计数不会变为 0,因为它们互相持有对方的引用,从而导致内存泄漏。

    为了避免循环引用和内存泄漏,我们可以使用弱引用和无主引用来打破引用循环。

    1. 弱引用和无主引用

    弱引用是一种不会增加对象引用计数的引用,它通常用于解决循环引用的问题。在 Swift 中,我们可以使用 weak 关键字来声明一个弱引用。

    例如,下面的代码展示了如何使用弱引用解决循环引用的问题:

    class Person {
        var name: String
        var pet: Pet?
        
        init(name: String) {
            self.name = name
            print("\(name) is being initialized.")
        }
        
        deinit {
            print("\(name) is being deinitialized.")
        }
    }
    
    class Pet {
        var name: String
        weak var owner: Person?
        
        init(name: String) {
            self.name = name
            print("\(name) is being initialized.")
        }
        
        deinit {
            print("\(name) is being deinitialized.")
        }
    }
    
    var person: Person?
    var pet: Pet?
    
    person = Person(name: "Alice")
    pet = Pet(name: "Fluffy")
    
    person?.pet = pet
    pet?.owner = person
    
    person = nil
    pet = nil
    
    // 输出 "Alice is being deinitialized."
    // 输出 "Fluffy is being deinitialized."
    

    在上面的例子中,我们将 Pet 类的 owner 属性声明为一个弱引用,这样就不会增加 Person 对象的引用计数。当我们将 person 和 pet 的引用都置为 nil 时,它们的引用计数都变为 0,从而释放了内存。

    另外,无主引用是一种类似于弱引用的引用,但是它假设引用的对象始终存在,不需要在对象释放时设置为 nil。在 Swift 中,我们可以使用 unowned 关键字来声明一个无主引用。

    例如,下面的代码展示了如何使用无主引用解决循环引用的问题:

    class Customer {
        var name: String
        var creditCard: CreditCard?
        
        init(name: String) {
            self.name = name
            print("\(name) is being initialized.")
        }
        
        deinit {
            print("\(name) is being deinitialized.")
        }
    }
    
    class CreditCard {
        let number: Int
        unowned let customer: Customer
        
        init(number: Int, customer: Customer) {
            self.number = number
            self.customer = customer
            print("Credit card #\(number) is being initialized.")
        }
        
        deinit {
            print("Credit card #\(number) is being deinitialized.")
        }
    }
    
    var customer: Customer?
    customer = Customer(name: "Alice")
    
    customer!.creditCard = CreditCard(number: 1234_5678_9012_3456, customer: customer!)
    
    customer = nil
    
    // 输出 "Alice is being deinitialized."
    // 输出 "Credit card #1234567890123456 is being deinitialized."
    

    在上面的例子中,我们将 CreditCard 类的 customer 属性声明为一个无主引用,这样就可以避免循环引用的问题。当我们将 customer 的引用置为 nil 时,它的引用计数变为 0,从而释放了内存。

    总结
    Swift 通过使用自动引用计数(ARC)来管理内存,我们只需要关注对象之间的引用关系即可。然而,当存在循环引用时,ARC 无法自动释放内存,可能导致内存泄漏。为了避免循环引用和内存泄漏,我们可以使用弱引用和无主引用来打破引用循环,从而释放内存。在编写 Swift 代码时,我们应该注意引用关系,避免出现循环引用和内存泄漏的情况。

    相关文章

      网友评论

        本文标题:7、Swift 中的内存管理

        本文链接:https://www.haomeiwen.com/subject/rsqdjdtx.html