Sobes.tech
Middle+

É sempre necessário usar weak ou unowned na lista de captura?

sobes.tech IA

Resposta da IA

Não, nem sempre.

Weak e unowned são usados para evitar ciclos de referências fortes ( ciclos de retenção) em closures, quando a closure mantém uma referência forte a um objeto, e o objeto por sua vez mantém uma referência forte à closure.

É necessário usar weak ou unowned quando a closure captura uma referência forte a self ou a outro tipo referencial, e há probabilidade de ocorrer um ciclo de referências fortes.

Situações em que não é necessário usar weak/unowned:

  • A closure captura tipos de valor (estruturas, enumerações, tuplas). Elas são copiadas na captura, e ciclos de referências fortes não ocorrem.
  • A closure captura um tipo referencial, mas não há uma referência forte bidirecional. Por exemplo, se a closure é criada localmente dentro de um método e não é armazenada em um objeto ao qual faz referência.
  • A closure tem uma vida curta e será executada antes que o objeto ao qual faz referência seja liberado. Por exemplo, uma closure passada para um método como um manipulador de conclusão que é executado imediatamente.

Escolha entre weak e unowned:

  • weak: usado quando o objeto capturado pode se tornar nil a qualquer momento antes da execução da closure. A referência é declarada como opcional (ObjectType?). Requer verificação de nil antes de usar.
  • unowned: usado quando se sabe que o objeto capturado sempre existirá no momento da execução da closure. A referência não é opcional (ObjectType). Não requer verificação de nil. Usar unowned para um objeto que se tornou nil causará uma falha (erro em tempo de execução).
// Exemplo onde não é necessário weak/unowned (captura de estrutura)
struct MyStruct {
    var value: Int
}

let myStruct = MyStruct(value: 10)
let closure = {
    print(myStruct.value) // Captura uma cópia de myStruct
}
closure()

// Exemplo onde não é necessário weak/unowned (closure local)
class MyClass {
    func doSomething() {
        let localValue = 5
        let localClosure = {
            print(self.description) // Captura self, mas a closure não é armazenada em MyClass
        }
        localClosure()
    }

    var description: String {
        return "Instance of MyClass"
    }
}

let myObject = MyClass()
myObject.doSomething()

// Exemplo onde é necessário weak (potencialmente nil)
class AsyncOperation {
    var completionHandler: (() -> Void)?

    func performOperation() {
        // Simulação de trabalho assíncrono
        DispatchQueue.global().async {
            print("Operation completed")
            self.completionHandler?() // encadeamento opcional
        }
    }

    deinit {
        print("AsyncOperation deinitialized")
    }
}

class ViewController {
    var op: AsyncOperation?

    func setupOperation() {
        op = AsyncOperation()
        op?.completionHandler = { [weak self] in // weak self
            guard let self = self else { return }
            print("Completion handler in ViewController: \(self)")
        }
    }

    deinit {
        print("ViewController deinitialized")
    }
}

// Exemplo onde é necessário unowned (garantidamente existe)
class Button {
    var action: (() -> Void)?

    func tap() {
        action?()
    }

    deinit {
        print("Button deinitialized")
    }
}

class ViewControllerWithButton {
    var button: Button

    init() {
        button = Button()
        button.action = { [unowned self] in // unowned self
            print("Button tapped in ViewControllerWithButton: \(self)")
        }
    }

    deinit {
        print("ViewControllerWithButton deinitialized")
    }
}