Jak działa mechanizm automatycznego zarządzania pamięcią (ARC) w języku Swift?
sobes.tech AI
Odpowiedź od AI
ARC (Automatyczne Liczenie Referencji) automatycznie śledzi i zarządza odwołaniami do instancji klas. Zwolni pamięć zajmowaną przez instancję, gdy nie ma już do niej silnych odwołań. ARC działa tylko z instancjami klas, nie z strukturami czy wyliczeniami, ponieważ są to typy wartości.
Trzy typy powiązań między instancjami:
- Strong (Silne): Powiązanie, które zwiększa licznik odwołań do instancji. Domyślnie wszystkie odwołania są silne.
- Weak (Słabe): Powiązanie, które nie zwiększa licznika odwołań. Używane do zapobiegania cyklom silnych odwołań. Odwołania typu
weaksą zawsze opcjonalne, ponieważ instancja może zostać zwolniona w dowolnym momencie. - Unowned (Bez właściciela): Powiązanie, które nie zwiększa licznika odwołań i jest używane, gdy jest gwarancja, że powiązana instancja będzie istnieć dłużej niż bieżąca. Nie jest obowiązkowe, ale próba dostępu do zwolnionej instancji spowoduje błąd wykonania.
Cykl silnych odwołań powstaje, gdy dwie lub więcej instancji klasy mają silne odwołania do siebie, uniemożliwiając ich zwolnienie przez ARC. Aby rozwiązać cykle, używa się odwołań weak lub unowned.
Przykład cyklu silnych odwołań:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit { // wywoływane przy zwolnieniu
print("\(name) jest deinitializowany")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
}
deinit { // wywoływane przy zwolnieniu
print("Apartment \(unit) jest deinitializowany")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil
unit4A = nil // Nic nie jest drukowane, ponieważ istnieje cykl silnych odwołań
Rozwiązanie cyklu z weak:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit {
print("\(name) jest deinitializowany")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Słabe odwołanie do Person
init(unit: String) {
self.unit = unit
}
deinit {
print("Apartment \(unit) jest deinitializowany")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil // Zwolnienie Person
unit4A = nil // Zwolnienie Apartment
Rozwiązanie z unowned:
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit {
print("\(name) jest deinitializowany")
}
}
class CreditCard {
let number: UInt64
unowned let customer: Customer // Rzecz bez właściciela
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit {
print("Karta #\(number) jest deinitializowana")
}
}
var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
john = nil // Zwolnienie Customer i CreditCard
ARC w zamknięciach (closures): Zamknięcia mogą również tworzyć cykle silnych odwołań, przechwytując instancje klas. Aby tego uniknąć, używa się list przechwytywania ([weak self] lub [unowned self]).
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = { // lazy, aby móc używać self przed inicjalizacją
[unowned self] in // Lista przechwytywania z "unowned 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) jest deinitializowany")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // Zwolnienie HTMLElement