Skip to content

Commit bf25ac1

Browse files
committed
Improve discussion and examples for alternatives to unowned captures
1 parent fab63dd commit bf25ac1

File tree

1 file changed

+59
-33
lines changed

1 file changed

+59
-33
lines changed

README.md

Lines changed: 59 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,50 +1622,76 @@ _You can enable the following settings in Xcode by running [this script](resourc
16221622

16231623
</details>
16241624

1625-
* <a id='unowned-captures'></a>(<a href='#unowned-captures'>link</a>) **Prefer using `weak` captures over `unowned` captures.** [![SwiftLint: unowned_variable_capture](https://img.shields.io/badge/SwiftLint-unowned__variable__capture-007A87.svg)](https://realm.github.io/SwiftLint/unowned_variable_capture.html)
1625+
* <a id='unowned-captures'></a>(<a href='#unowned-captures'>link</a>) **Avoid using `unowned` captures.** Instead prefer safer alternatives like `weak` captures, or capturing variables directly. [![SwiftLint: unowned_variable_capture](https://img.shields.io/badge/SwiftLint-unowned__variable__capture-007A87.svg)](https://realm.github.io/SwiftLint/unowned_variable_capture.html)
16261626

16271627
<details>
1628-
`unowned` captures are unsafe because they will cause the application to crash if the referenced object has been deallocated. `weak` captures are safer because they require the author to explicitly handle the case where the referenced object no longer exists.
1628+
`unowned` captures are unsafe because they will cause the application to crash if the referenced object has been deallocated.
16291629

16301630
```swift
1631-
// WRONG: Crashes if `planet` has been deallocated when the closure is called.
1632-
1633-
spaceship.travel(to: planet, onArrival: { [unowned planet] in
1634-
planet.colonize()
1635-
})
1636-
1637-
spaceship.travel(to: planet, nextDestination: { [unowned planet] in
1638-
planet.moons.first ?? planet.sun
1639-
})
1631+
// WRONG: Crashes if `self` has been deallocated when closures are called.
1632+
final class SpaceshipNavigationService {
1633+
let spaceship: Spaceship
1634+
let planet: Planet
1635+
1636+
func colonizePlanet() {
1637+
spaceship.travel(to: planet, onArrival: { [unowned self] in
1638+
planet.colonize()
1639+
})
1640+
}
1641+
1642+
func exploreSystem() {
1643+
spaceship.travel(to: planet, nextDestination: { [unowned self] in
1644+
planet.moons?.first
1645+
})
1646+
}
1647+
}
16401648
```
1649+
1650+
`weak` captures are safer because they require the author to explicitly handle the case where the referenced object no longer exists.
16411651

16421652
```swift
1643-
// RIGHT: Explicitly handles case where `planet` has been deallocated.
1644-
1645-
spaceship.travel(to: planet, onArrival: { [weak planet] in
1646-
guard let planet else { return }
1647-
planet.colonize()
1648-
})
1649-
1650-
// For closures that return a non-optional value, you could either
1651-
// use `fatalError` to avoid having to return anything:
1652-
spaceship.travel(to: planet, nextDestination: { [weak planet] in
1653-
guard let planet else {
1654-
fatalError("Planet was unexpectedly deallocated before reached by spaceship")
1653+
// RIGHT: Uses a `weak self` capture and explicitly
1654+
// handles the case where `self` has been deallocated
1655+
final class SpaceshipNavigationService {
1656+
let spaceship: Spaceship
1657+
let planet: Planet
1658+
1659+
func colonizePlanet() {
1660+
spaceship.travel(to: planet, onArrival: { [weak self] in
1661+
guard let self else { return }
1662+
planet.colonize()
1663+
})
16551664
}
16561665

1657-
return planet.moons.first ?? planet.sun
1658-
})
1659-
1660-
// Or you could return a placeholder value with an optional `assertionFailure`:
1661-
spaceship.travel(to: planet, nextDestination: { [weak planet] in
1662-
guard let planet else {
1663-
assertionFailure("Planet was unexpectedly deallocated before reached by spaceship")
1664-
return Planet()
1666+
func exploreSystem() {
1667+
spaceship.travel(to: planet, nextDestination: { [weak self] in
1668+
guard let self else { return nil }
1669+
return planet.moons?.first
1670+
})
1671+
}
1672+
}
1673+
```
1674+
1675+
Alternatively, consider directly capturing the variables that are used in the closure. This lets you avoid having to handle the case where `self` is nil, since you don't even need to reference `self`:
1676+
1677+
```swift
1678+
// RIGHT: Explicitly captures `planet` instead of capturing `self`
1679+
final class SpaceshipNavigationService {
1680+
let spaceship: Spaceship
1681+
let planet: Planet
1682+
1683+
func colonizePlanet() {
1684+
spaceship.travel(to: planet, onArrival: { [planet] in
1685+
planet.colonize()
1686+
})
16651687
}
16661688

1667-
return planet.moons.first ?? planet.sun
1668-
})
1689+
func exploreSystem() {
1690+
spaceship.travel(to: planet, nextDestination: { [planet] in
1691+
planet.moons?.first
1692+
})
1693+
}
1694+
}
16691695
```
16701696

16711697
</details>

0 commit comments

Comments
 (0)