Como funciona o mecanismo de gestão automática de memória (ARC) na linguagem Swift?
sobes.tech IA
Resposta da IA
ARC (Contagem Automática de Referências) rastreia e gere automaticamente as referências às instâncias de classes. Liberta a memória ocupada por uma instância quando já não há referências fortes a ela. O ARC funciona apenas com instâncias de classes, não com estruturas ou enumerações, pois são tipos de valor.
Três tipos de ligações entre instâncias:
- Strong (Forte): Uma ligação que aumenta o contador de referências da instância. Por padrão, todas as ligações são fortes.
- Weak (Fraca): Uma ligação que não aumenta o contador de referências. Usada para evitar ciclos de referências fortes. As ligações do tipo
weaksão sempre opcionais, pois a instância pode ser libertada a qualquer momento. - Unowned (Sem proprietário): Uma ligação que não aumenta o contador de referências e é usada quando se garante que a instância relacionada existirá por mais tempo que a atual. Não é obrigatório, mas tentar aceder a uma instância libertada provocará um erro em tempo de execução.
Os ciclos de referências fortes ocorrem quando duas ou mais instâncias de classes têm referências fortes entre si, impedindo a sua libertação pelo ARC. Para resolver estes ciclos, usam-se referências weak ou unowned.
Exemplo de ciclo de referências fortes:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit { // chamado na libertação
print("\(name) está a ser desalojado")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
}
deinit { // chamado na libertação
print("Apartamento \(unit) está a ser desalojado")
}
}
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 // Nada é impresso, pois há um ciclo de referências fortes
Resolução do ciclo com weak:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit {
print("\(name) está a ser desalojado")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Referência fraca a Person
init(unit: String) {
self.unit = unit
}
deinit {
print("Apartamento \(unit) está a ser desalojado")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil // Liberta a Person
unit4A = nil // Liberta o Apartment
Resolução com unowned:
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit {
print("\(name) está a ser desalojado")
}
}
class CreditCard {
let number: UInt64
unowned let customer: Customer // Referência sem proprietário a Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit {
print("Cartão #\(number) está a ser desalojado")
}
}
var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
john = nil // Liberta Customer e CreditCard
ARC em closures: As closures também podem criar ciclos de referências fortes, capturando instâncias de classes. Para evitar isso, usam-se listas de captura ([weak self] ou [unowned self]).
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = { // lazy para poder usar self antes da inicialização
[unowned self] in // Lista de captura com "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) está a ser desalojado")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // Liberta HTMLElement