Middle
Opowiedz o zarządzaniu pamięcią w iOS.
sobes.tech AI
Odpowiedź od AI
iOS używa automatycznego zliczania odwołań (ARC) do zarządzania pamięcią. ARC automatycznie zwalnia obiekty, gdy przestają być odwoływane.
Główne koncepcje:
- Silne odwołanie: Zwiększa licznik odwołań obiektu. Obiekt nie zostanie zwolniony, dopóki istnieją silne odwołania.
- Słabe odwołanie: Nie zwiększa licznika odwołań. Używane do zapobiegania cyklom silnych odwołań. Odwołuje się do obiektu, który może zostać zwolniony. Jeśli obiekt zostanie zwolniony, słabe odwołanie staje się
nil. - Nieposiadane odwołanie (unowned): Podobne do słabego, nie zwiększa licznika odwołań. Używane, gdy obiekt, na który się odwołuje, ma taki sam lub dłuższy cykl życia. Nieposiadane odwołanie nie staje się
nilpo zwolnieniu obiektu. Próba dostępu do zwolnionego obiektu przez nieposiadane odwołanie powoduje błąd wykonania.
Cykle silnych odwołań (Retain Cycles): Powstają, gdy dwa lub więcej obiektów odwołuje się do siebie nawzajem za pomocą silnych odwołań, uniemożliwiając ich zwolnienie. Rozwiązaniem są słabe lub nieposiadane odwołania.
Przykład cyklu silnych odwołań i jego rozwiązanie:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) jest inicjalizowany")
}
deinit {
print("\(name) jest deinitializowany")
}
}
class Apartment {
let unit: String
var tenant: Person? // Silne odwołanie powodujące cykl
init(unit: String) {
self.unit = unit
print("Mieszkanie \(unit) jest inicjalizowane")
}
deinit {
print("Mieszkanie \(unit) jest deinitializowane")
}
}
var john: Person? = Person(name: "John Appleseed")
var unit4A: Apartment? = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john // Tworzy cykl
john = nil
unit4A = nil
// Metody deinitializacji nie są wywoływane z powodu cyklu
Rozwiązanie z użyciem weak:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) jest inicjalizowany")
}
deinit {
print("\(name) jest deinitializowany")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Użycie słabego odwołania
init(unit: String) {
self.unit = unit
print("Mieszkanie \(unit) jest inicjalizowane")
}
deinit {
print("Mieszkanie \(unit) jest deinitializowane")
}
}
var john: Person? = Person(name: "John Appleseed")
var unit4A: Apartment? = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil
unit4A = nil
// Oba obiekty są poprawnie deinitializowane
Nieposiadane (unowned) vs Słabe (weak):
- Używaj
weak, gdy odwołanie może stać sięnil(obiekt może zostać zwolniony jako pierwszy). - Używaj
unowned, gdy odwołanie zawsze będzie miało wartość (obiekt, na który się odwołujesz, ma taki sam lub dłuższy cykl życia).
Zamykania (Closures) i cykle odwołań:
Zamykania mogą przechwytywać odwołania do obiektów, tworząc cykle silnych odwołań. Rozwiązuje się to za pomocą listy przechwytywania (capture list).
Przykład:
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
[weak self] in // Przechwytywanie słabe self
guard let self = self else {
return ""
}
var html = "<\(self.name)"
if let text = self.text {
html += ">\(text)</\(self.name)>"
} else {
html += " />"
}
return html
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) jest deinitializowany")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "cześć, świecie")
print(paragraph!.asHTML())
paragraph = nil
// Obiekt jest deinitializowany
Debugowanie problemów z pamięcią:
- Memory Graph Debugger: W Xcode, pozwala wizualizować powiązania między obiektami i wykrywać cykle silnych odwołań.
- Instruments (Allocations, Leaks): Narzędzia do monitorowania przydziału i zwalniania pamięci, wykrywania wycieków.