Swift 使用自动引用计数(Automatic Reference Counting,ARC)来管理应用程序内存的使用。ARC 是一种内存管理机制,负责跟踪和计算类实例的引用,确保当实例不再被使用时,其所占用的内存能够被正确释放。

如何工作

每个类实例都有一个引用计数,表示有多少个对象引用了这个实例。当引用计数变为零时,实例所占用的内存就会被释放。

  •  增加引用计数:当一个新的引用指向实例时,引用计数会加一。

  •  减少引用计数:当一个引用不再指向实例时,引用计数会减一。


Swift 使用强引用来管理引用计数,这意味着只要存在对实例的强引用,实例就不会被释放。

强引用

当你创建一个类的实例并将其赋值给一个变量或常量时,这个变量或常量就成为对实例的强引用。只要这个强引用还存在,实例就不会被释放。
class Person {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

var person1: Person? = Person(name: "John")
var person2: Person? = person1  // 增加了一个强引用

person1 = nil  // 引用计数减一
person2 = nil  // 引用计数减一,实例被释放

循环引用

当两个或多个实例相互保持强引用时,可能会形成循环引用。这会导致引用计数永远不会变为零,实例也永远不会被释放,从而造成内存泄漏。

Swift 提供了弱引用和无主引用来解决循环引用的问题。

弱引用和无主引用

  •  弱引用(Weak References):不会保持实例的引用计数,当被引用的实例被释放时,弱引用会自动变为 nil。

  
  class Apartment {
      var tenant: Person?
  }
  
  class Person {
      var apartment: Apartment?
  }
  
  var john: Person? = Person()
  var unit4A: Apartment? = Apartment()
  
  // 弱引用
  john?.apartment = unit4A
  unit4A?.tenant = john
  john = nil  // 引用计数减一,但因为是弱引用,不会影响引用计数
  print(unit4A?.tenant)  // 输出 nil

  •  无主引用(Unowned References):也不会保持实例的引用计数,但假定被引用的实例在整个生命周期中不会被释放,否则可能引起运行时错误。

  class Customer {
      var card: CreditCard?
      
      deinit {
          print("Customer deinitialized")
      }
  }
  
  class CreditCard {
      // 无主引用
      unowned let customer: Customer
      
      init(customer: Customer) {
          self.customer = customer
      }
      
      deinit {
          print("CreditCard deinitialized")
      }
  }
  
  var john: Customer? = Customer()
  john?.card = CreditCard(customer: john!)
  john = nil  // 引用计数减一,但由于无主引用,不会影响引用计数,CreditCard 的实例会被正确释放

闭包引起的循环引用

当闭包捕获了实例时,也可能导致循环引用。在闭包中,使用 [unowned self] 或 [weak self] 来避免这种情况。
class HTMLElement {
    let name: String
    var text: String?
    
    lazy var asHTML: () -> String = {
        [unowned self] in  // 或者 [weak 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) is being deinitialized")
    }
}

var element: HTMLElement? = HTMLElement(name: "div", text: "Hello, World!")
print(element?.asHTML() ?? "")  // 输出 "<div>Hello, World!</div>"
element = nil  // 引用计数减一,HTMLElement 实例被释放

以上是一些关于 Swift 自动引用计数的基本用法。ARC 简化了内存管理,使得开发者无需手动管理内存,从而减少了内存泄漏和野指针的发生。


转载请注明出处:http://www.zyzy.cn/article/detail/14436/Swift