Weak Self In Closure

ID

swift.weak_self_in_closure

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Swift

Tags

memory-leak, reliability, suspicious-code

Description

Reports a closure that is passed to a known-escaping Cocoa / Foundation API and captures self strongly. The closure outlives the call, so a strong capture pins self alive at least until the closure runs. When the closure (or the work it kicks off) is also retained by self, the two references form a cycle that ARC cannot break.

Task {
    self.refresh()                  // FLAW — strong self
}

DispatchQueue.main.async {
    self.update()                   // FLAW — strong self
}

Task { [weak self] in
    self?.refresh()                 // OK
}

Task { [unowned self] in
    self.refresh()                  // OK
}

Rationale

Closures retain captured references strongly by default. The allow-list of APIs the rule looks at — Task, DispatchQueue.async, DispatchQueue.asyncAfter, URLSession.dataTask / downloadTask / uploadTask, schedule — store their closure for later execution, so the captured self survives past the enclosing function. Combined with a closure that is itself held by self (a stored property, a delegate handler, a subscription) the chain forms a retain cycle. The process keeps the object graph alive for the lifetime of the program.

The rule scopes conservatively to that fixed allow-list. Precise escaping-closure detection requires @escaping attribute resolution across module boundaries, which is not available in the public AST. The allow-list catches the common offenders without the false positives of a blanket "closure references `self`" check.

Remediation

Add a capture list that holds self weakly (the usual choice) or unowned when self is guaranteed to outlive the closure:

Task { [weak self] in
    self?.refresh()
}

DispatchQueue.main.async { [unowned self] in
    self.update()
}

If the closure body does not need self, refactor so it does not reference it at all.