Fale-nos sobre a gestão de memória no iOS.
sobes.tech IA
Resposta da IA
O iOS utiliza contagem automática de referências (ARC) para gerir a memória. O ARC liberta automaticamente os objetos assim que deixam de ser referenciados.
Conceitos principais:
- Referência forte: Aumenta o contador de referências do objeto. O objeto não será libertado enquanto existirem referências fortes.
- Referência fraca: Não aumenta o contador de referências. Usada para evitar ciclos de referências fortes. Refere-se a um objeto que pode ser libertado. Se o objeto for libertado, a referência fraca torna-se
nil. - Referência não possuída (unowned): Semelhante à fraca, não aumenta o contador de referências. Usada quando o objeto referenciado tem um ciclo de vida igual ou mais longo. A referência não possuída não se torna
nilao libertar o objeto. Tentar aceder a um objeto libertado através de uma referência não possuída gera um erro em tempo de execução.
Ciclos de referências fortes (Retain Cycles): Ocorrem quando dois ou mais objetos referenciam-se mutuamente com referências fortes, impedindo que sejam libertados. São resolvidos usando referências fracas ou não possuídas.
Exemplo de ciclo de referências fortes e a sua solução:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) está a ser inicializado")
}
deinit {
print("\(name) está a ser desinicializado")
}
}
class Apartment {
let unit: String
var tenant: Person? // Referência forte que causa ciclo de retenção
init(unit: String) {
self.unit = unit
print("Apartamento \(unit) está a ser inicializado")
}
deinit {
print("Apartamento \(unit) está a ser desinicializado")
}
}
var john: Person? = Person(name: "John Appleseed")
var unit4A: Apartment? = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john // Aqui ocorre um ciclo de retenção
john = nil
unit4A = nil
// Os métodos de desinicialização não são chamados devido ao ciclo de retenção
Solução usando weak:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) está a ser inicializado")
}
deinit {
print("\(name) está a ser desinicializado")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Usa referência weak para evitar ciclo de retenção
init(unit: String) {
self.unit = unit
print("Apartamento \(unit) está a ser inicializado")
}
deinit {
print("Apartamento \(unit) está a ser desinicializado")
}
}
var john: Person? = Person(name: "John Appleseed")
var unit4A: Apartment? = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil
unit4A = nil
// Ambos objetos são desinicializados corretamente
Não possuído (unowned) vs Fraco (weak):
- Usa
weakquando a referência pode tornar-senil(o objeto pode ser libertado primeiro). - Usa
unownedquando a referência sempre terá um valor (o objeto referenciado tem um ciclo de vida igual ou mais longo).
Closures e ciclos de referências:
As closures podem capturar referências a objetos, criando ciclos de referências fortes. Isto resolve-se com uma lista de captura (capture list).
Exemplo:
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
[weak self] in // Captura fraca de 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) está a ser desinicializado")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "olá, mundo")
print(paragraph!.asHTML())
paragraph = nil
// O objeto é desinicializado
Depuração de problemas de memória:
- Memory Graph Debugger: No Xcode, permite visualizar as ligações entre objetos e identificar ciclos de referências fortes.
- Instruments (Allocations, Leaks): Ferramentas para monitorizar a alocação e libertação de memória, e detectar fugas.