Published 2022-03-25

Swift - ARC(内存管理机制)

Automatic Reference Counting(ARC)是一种内存管理机制,它是由苹果公司在其 Objective-C 和 Swift 语言中使用的。ARC 的作用是在程序运行时自动管理对象的内存,从而避免了手动管理内存的繁琐和容易出错的问题。

在 Objective-C 中,ARC 是默认开启的,它通过计算对象的引用计数来自动管理对象的内存。每当一个对象被创建时,它的引用计数会被初始化为 1,每当一个对象被引用时,它的引用计数会增加 1,每当一个对象不再被引用时,它的引用计数会减少 1。当一个对象的引用计数为 0 时,它就会被自动释放掉。

在 Swift 中,ARC 同样是默认开启的,它采用了和 Objective-C 相同的引用计数机制。不同的是,在 Swift 中,我们可以使用强引用、弱引用和无主引用来管理对象的内存。强引用是最常见的引用类型,它会使对象的引用计数增加 1,并且只有在没有任何强引用指向对象时,对象的引用计数才会减少 1;弱引用和无主引用则可以用来避免循环引用的问题。

在使用 ARC 时,我们只需要遵循一些基本规则即可避免内存泄漏和野指针等问题。例如,我们可以使用 weak 或 unowned 来避免循环引用的问题;我们不应该手动管理内存,例如使用 retain、release 和 autorelease 等方法;我们需要避免在闭包中持有 self 等对象,以避免循环引用的问题等。

除了基本规则外,ARC 还提供了一些高级特性,例如自动闭包和无主引用等。下面我们来看一些常见的 ARC 特性。

自动闭包

自动闭包是一种方便的语法结构,它可以让我们把一个表达式封装成一个闭包,并在需要时自动调用它。在 Swift 中,我们可以使用 @autoclosure 关键字来声明一个自动闭包。例如:

func printIfTrue(_ predicate: @autoclosure () -> Bool) {
  if predicate() {
    print("True")
  } else {
    print("False")
  }
}

printIfTrue(2 + 2 == 4)

在上面的代码中,我们定义了一个 printIfTrue 函数,它接受一个返回 Bool 类型的自动闭包作为参数,并在需要时自动调用它。在调用该函数时,我们可以传递一个表达式,例如 2 + 2 == 4,这个表达式会被自动封装成一个闭包,并在 printIfTrue 函数中被调用。

无主引用

无主引用是一种特殊的弱引用,它用来避免弱引用在使用时被意外释放的问题。在 Swift 中,我们可以使用 unowned 关键字来声明一个无主引用。例如:

class Person {
  var name: String
  var apartment: Apartment?

    init(name: String) {
        self.name = name
    }

    deinit {
        print("\(name) is being deinitialized")
    }
}

class Apartment {
  let number: Int
  weak var tenant: Person?

    init(number: Int) {
        self.number = number
    }

    deinit {
        print("Apartment #\(number) is being deinitialized")
    }
}

var john: Person?
var number73: Apartment?

john = Person(name: "John")
number73 = Apartment(number: 73)

john!.apartment = number73
number73!.tenant = john

john = nil
number73 = nil

在上面的代码中,我们定义了一个 Person 类和一个 Apartment 类,它们之间通过弱引用和无主引用进行关联。具体来说,Person 类中的 apartment 属性是一个可选的 Apartment 类型,并且它是通过弱引用来实现的;而 Apartment 类中的 tenant 属性是一个可选的 Person 类型,并且它是通过无主引用来实现的。这样,在我们释放 john 和 number73 变量所占用的内存时,它们之间的引用关系就可以正确地被解除。

总的来说,ARC 是一种非常方便和安全的内存管理机制,它可以自动管理对象的内存,从而避免了手动管理内存的繁琐和容易出错的问题。在使用 ARC 时,我们只需要遵循一些基本规则和高级特性即可,从而让我们更加专注于应用程序的业务逻辑。