Introduction
We propose the explicit declaration of [strong someVar]
the same way we declare [weak someVar]
when capturing variables in a block scope.
If a "reference type variable" is captured in the block without explicitly declaring it as strong
or weak
the compiler produces a warning to alert the developer of an unintended strong
capture of a "reference type variable".
Motivation
The most common memory-related issue we encounter when working with swift is that variables are implicitly captured in blocks scope when we did not intend them to be strongly captured.
This issue makes the use of blocks so error-prone that we prefer to avoid using it in spite of it being much more readable and more convenient than using delegates.
Consider the following common pattern:
func collectionView(_: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
...
cell.didSelectLinkBlock = { [weak self] (link) in
self?.presentWebViewController(with: link, sender: cell)
}
...
}
The easily overlooked memory retain cycle is that cell
is strongly captured in the block, which is stored in the cell to be called when links are selected, so the cell keeps a reference to itself, creating a memory cycle.
Proposed solution
We are proposing to explicitly declare every variable that is strongly captured in a block to avoid unintentionally strongly capturing variables in the block.
The solution would be applied to the previous example in the following way:
func collectionView(_: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
...
cell.didSelectLinkBlock = { [weak self, strong cell] (link) in
self?.presentWebViewController(with: link, sender: cell)
}
...
}
Now it is clear that we are capturing cell
intentionally, so it is easier to see the memory cycles and avoid it if necessary.
Effect on ABI stability
The proposed solution maintains ABI stability, and the change only produces warnings for reference type variables that are implicitly captured in blocks, which is what we want.
Edit: Feedback points
Some of the great feedback points we got so far include the following:
- Scaling down the proposal to only produce warnings when reference type variables are implicitly captured in a block.
- To explicitly declare a strong capture of a reference type we can use the current ability to add the variable to the capture list without adding the strong keyword
- Only apply the proposal to blocks that are declared as @escaping
- Not applying the proposal to nested reference type variables