I'm using RxSwift in a project and I found that when I bind directly to a selector it captures a strong reference from self and deinit wasn't called.
I was wondering how to make selector/func to deal with only a weak reference of self.
viewModel.title
.drive(onNext: updateTitle)
.disposed(by: disposeBag)
func updateTitle(_ title: String) {
navigationItem.title = title
}
What I've tried is
func updateTitle(_ title: String) {
weak var weakSelf = self
weakSelf?.navigationItem.title = title
}
But still deinit is not getting called.
Of course one solution would be to remove the function entirely
viewModel.title
.drive(onNext: { [weak self] title in
self?.updateTitle(title)
)
.disposed(by: disposeBag)
but I don't to lose the concise binding code.
Playing around with it, I found that you can get a syntax like:
viewModel.title
.drive(onNext: weakCapture(self, method: YourViewController.updateTitle))
.disposed(by: disposeBag)
The medicine is worse than the disease, but it's a bit of fun. Here's the definition of the helper method:
func weakCapture<T: AnyObject, A1>(
_ target: T,
method: #escaping (T) -> (A1) -> Void
) -> (A1) -> Void {
return { [weak target] arg in
guard let strongTarget = target else { return }
method(strongTarget)(arg)
}
}
Here's an example usage:
var c: C? = C()
let weaklyCapturedFooMethod = weakCapture(c!, method: C.foo)
weaklyCapturedFooMethod(123) // Runs foo(i: 123)
print(c as Any)
c = nil
weaklyCapturedFooMethod(123) // does nothing
It's not very nice. :P
I would recommend just using: { [weak self] self?.updateTitle($0) }
After some looking, apparently you can't make a func weak because it is not saved in the memory, on the other hand closures capture the environment around it.
So my solution was to create a closure as a var:
viewModel.title
.drive(onNext: updateTitle)
.disposed(by: disposeBag)
lazy var updateTitle: ((String) -> Void)? = { [weak self] _ in
self?.navigationItem.title = $0
}
It is a solution but still curious if we can make the selector captures weak reference.
Related
I need to keep strong self inside my inner clousures.
I know that it's enough to declare [weak self] only once for outer closure.
But what about guard let self = self else { return }, is it enough to declare it once for outer closure also? Do we have any edge cases here ?
apiManager.doSomething(user: user) { [weak self] result in
guard let self = self else { return }
self.storageManager.doSomething(user: user) { result in
// guard let self = self else { return } <- DO WE NEED IT HERE ?
self.doSomething()
}
}
Seems like language analyser says NO - one declaration is enough , but want to be sure.
Yes, one is enough.
If you write
guard let self = self else { return }
you'll create a new local variable that will hold a strong reference to the outside weak self.
It's the same as writing
guard let strongSelf = self else { return }
and then use strongSelf for the rest of the block.
Update Swift 5.7
You don't need a new guard in doSomething(user:), when that method works synchronously.
With Swift 5.7, you can use the rewrite syntax guard let self else
apiManager.doSomething(user: user) { [weak self] result in
guard let self else { return }
self.storageManager.doSomething(user: user) { result in
// You don't a new guard here, when doSomething works synchronously
self.doSomething()
}
}
But, when your guard only returns, than you could also write it so:
Same behaviour.
apiManager.doSomething(user: user) { [weak self] _ in
self?.storageManager.doSomething(user: user) { _ in
self?.doSomething()
}
}
No, in short you do not need that. If you outer closure uses [weak self] then you should not worry about the inner closure as it will already have a weak reference to self. On the other hand if your outer closure is not using [weak self] it is reasonable to put it for the inside block. A more detailed explation can be found here.
The safest approach is to use [weak self] 2 times in both outter and inner closure.
There is edge case, where using [weak self] 1 time will cause memory leak.
Consider the following escaping closure case, which keeps reference to closures (Example copied from https://stackoverflow.com/a/62352667/72437)
import UIKit
public class CaptureListExperiment {
public init() {
}
var _someFunctionWithTrailingClosure: (() -> ())?
var _anotherFunctionWithTrailingClosure: (() -> ())?
func someFunctionWithTrailingClosure(closure: #escaping () -> ()) {
print("starting someFunctionWithTrailingClosure")
_someFunctionWithTrailingClosure = closure
DispatchQueue.global().asyncAfter(deadline: .now() + 1) { [weak self] in
self?._someFunctionWithTrailingClosure!()
print("finishing someFunctionWithTrailingClosure")
}
}
func anotherFunctionWithTrailingClosure(closure: #escaping () -> ()) {
print("starting anotherFunctionWithTrailingClosure")
_anotherFunctionWithTrailingClosure = closure
DispatchQueue.global().asyncAfter(deadline: .now() + 1) { [weak self] in
self?._anotherFunctionWithTrailingClosure!()
print("finishing anotherFunctionWithTrailingClosure")
}
}
func doSomething() {
print("doSomething")
}
public func testCompletionHandlers() {
someFunctionWithTrailingClosure { [weak self] in
guard let self = self else { return }
self.anotherFunctionWithTrailingClosure {
self.doSomething()
}
}
}
// go ahead and add `deinit`, so I can see when this is deallocated
deinit {
print("deinit")
}
}
func performExperiment() {
let obj = CaptureListExperiment()
obj.testCompletionHandlers()
Thread.sleep(forTimeInterval: 1.3)
}
performExperiment()
/* Output:
starting someFunctionWithTrailingClosure
starting anotherFunctionWithTrailingClosure
finishing someFunctionWithTrailingClosure
doSomething
finishing anotherFunctionWithTrailingClosure
*/
Since, this kind of edge case is very hard to spot. I think using [weak self] 2 times, will be the safest approach and common pattern, without causing developer much headache.
I have a function with a completion handler, returning one parameter or more.
In a client, when executing a completion handler, I'd like to have an unowned reference to self, as well as having access to the parameter passed.
Here is the Playground example illustrating the issue and the goal I'm trying to achieve.
import UIKit
struct Struct {
func function(completion: (String) -> ()) {
completion("Boom!")
}
func noArgumentsFunction(completion: () -> Void) {
completion()
}
}
class Class2 {
func execute() {
Struct().noArgumentsFunction { [unowned self] in
//...
}
Struct().function { (string) in // Need [unowned self] here
//...
}
}
}
As I said in my comment
Struct().function { [unowned self] (string) in
//your code here
}
Capture list and closure parameters that should be the order in closures more info from Apple Documentation
Defining a Capture List
Each item in a capture list is a pairing of
the weak or unowned keyword with a reference to a class instance (such
as self) or a variable initialized with some value (such as delegate =
self.delegate!). These pairings are written within a pair of square
braces, separated by commas.
Place the capture list before a closure’s parameter list and return
type if they are provided:
lazy var someClosure: (Int, String) -> String = {
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
// closure body goes here
}
If a closure does not specify a parameter list or return type because
they can be inferred from
context, place the capture list at the very start of the closure,
followed by the in keyword:
lazy var someClosure: () -> String = {
[unowned self, weak delegate = self.delegate!] in
// closure body goes here
}
Is it just the syntax for including [unowned self] in the closure parameter list you need?
struct Struct {
func function(completion:(String)->()) {
completion("Boom!")
}
}
class Class {
func execute() {
Struct().function { [unowned self] string in
print(string)
print(self)
}
}
}
Class().execute()
I'm having trouble tracking down a retain cycle. I think it's to do with the way I subscribe to events. Pseudo code is like this:
override func viewDidLoad() {
func handleEvent() {
self.doSomething()
}
subscribe("eventName", block: handleEvent)
}
deinit {
unsubscribe("eventName")
}
Will this create a retain cycle to self / my ViewController? And if so, how can I get around it? If I was using a closure, I could use [weak self], but since I'm passing a function, is there anyway to use a [weak self] equivalent?
Long story short, your code does retain a reference. (handleEvent->viewDidLoad->self), http://blog.xebia.com/function-references-in-swift-and-retain-cycles/ has some general strategies to avoid the issue. My recommendation would be to create a function reference, rather than declaring a function:
let eventHandler: () -> () = { [weak self] in
self?.doSomething()
}
subscribe("eventName", block: eventHandler)
If you reference a property or method from inside your class it'll create a retain cycle.
class SomeClass {
val a: (block: (() -> ()) -> ()) = ...
func run() {
func b() {
print("Hello, World!")
}
func c() {
self.someMethod()
}
func d() { [weak self]
self?.someMethod()
}
a(block: b) // no retain cycle
a(block: c) // retain cycle
a(block: d) // no retain cycle
}
func someMethod() {
print("Hello, World!")
}
}
class Test {
private init() {}
static let shared = Test()
func test() {
}
}
let closure: ()->Void = {
Test.shared.test()
}
closure()
The code above is simple. But i want to know if I have to mark weak or unowned to the Singleton. And why?
No, because self is not used (explicitly or implicitly) in that closure.
If I have a closure passed to a function like this:
someFunctionWithTrailingClosure { [weak self] in
anotherFunctionWithTrailingClosure { [weak self] in
self?.doSomething()
}
}
If I declare self as [weak self] in someFunctionWithTrailingClosure's capture list without redeclaring it as weak again in the capture list of anotherFunctionWithTrailingClosure self is already becoming an Optional type but is it also becoming a weak reference as well?
Thanks!
The [weak self] in anotherFunctionWithTrailingClosure is not needed.
You can empirically test this:
class Experiment {
func someFunctionWithTrailingClosure(closure: #escaping () -> Void) {
print("starting", #function)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
closure()
print("finishing", #function)
}
}
func anotherFunctionWithTrailingClosure(closure: #escaping () -> Void) {
print("starting", #function)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
closure()
print("finishing", #function)
}
}
func doSomething() {
print(#function)
}
func testCompletionHandlers() {
someFunctionWithTrailingClosure { [weak self] in
self?.anotherFunctionWithTrailingClosure { // [weak self] in
self?.doSomething()
}
}
}
// go ahead and add `deinit`, so I can see when this is deallocated
deinit {
print("deinit")
}
}
And then:
func performExperiment() {
DispatchQueue.global().async {
let obj = Experiment()
obj.testCompletionHandlers()
// sleep long enough for `anotherFunctionWithTrailingClosure` to start, but not yet call its completion handler
Thread.sleep(forTimeInterval: 1.5)
}
}
If you do this, you will see that doSomething is never called and that deinit is called before anotherFunctionWithTrailingClosure calls its closure.
That having been said, I might still be inclined to use the [weak self] syntax on anotherFunctionWithTrailingClosure to make my intent explicit.
TL;DR
Although using [weak self] once in the outer block is fine (EX1), if you change this reference to strong (e.g. guard let self = self), you'll need a [weak self] in the inner block as well (EX3).
Also using [weak self] only once on the inner block is typically an error (EX2_B). Unfortunately, this is a common mistake to make when refactoring code, and can be hard-to-spot when it is made.
A good rule of thumb is to always use weak if the object is strong immediately outside of the closure.
Examples that don't retain self (i.e. typically these are the "good" scenarios):
// EX1
fn { [weak self] in
self?.foo()
}
// EX2
fn { [weak self] in
fn2 {
self?.foo()
}
}
// self is weak inside fn, thus adding an extra `[weak self]` inside fn2 is unnecessary
// EX3
fn { [weak self] in
guard let self = self else { return }
fn2 { [weak self] in
self?.foo()
}
}
Examples that DO retain self (i.e. typically the "bad" scenarios):
// EX1_B
fn {
self.foo()
}
// fn retains self
// EX2_B
fn {
fn2 { [weak self] in
self.foo()
}
}
// fn retains self (this is a common, hard-to-spot mistake)
// EX3_B
fn { [weak self] in
guard let self = self else { return }
fn2 {
self.foo()
}
}
// fn2 retains self
As Hamish alludes to, there are two main reasons weak is useful:
To prevent retain cycles.
To prevent objects living longer than they should be.
More on #2 (preventing long lived objects)
In Rob's example, the function is not retaining the closure (beyond dispatch_async which is all but guaranteed to fire the closure at some point in the future), thus you'll never end up with a retain cycle. So using weak in this case, then, is to prevent #2 from happening.
As Hamish mentions, weak is not actually needed in this example to prevent retain cycles, as there are no retain cycles. weak, in this case, is used to prevent an object living longer than needed. It depends entirely on your use-case as to when you consider an object living longer than needed. Thus there are times when you would want to use weak only outside (EX2), and other times when you would want to use the weak outer, strong inner, weak inner dance (EX3), for example.
More on #1 (preventing retain cycles)
To examine the retain cycle problem, let's say a function is storing a reference to the block (i.e. heap) instead of referencing the function directly (i.e. stack). Many times we don't know the internals of a class/function, so it's safer to assume that the function is retaining the block.
Now you can easily create a retain cycle using weak outer, and only using strong inner (EX3_B):
public class CaptureListExperiment {
public init() {
}
var _someFunctionWithTrailingClosure: (() -> ())?
var _anotherFunctionWithTrailingClosure: (() -> ())?
func someFunctionWithTrailingClosure(closure: #escaping () -> ()) {
print("starting someFunctionWithTrailingClosure")
_someFunctionWithTrailingClosure = closure
DispatchQueue.global().asyncAfter(deadline: .now() + 1) { [weak self] in
self?._someFunctionWithTrailingClosure!()
print("finishing someFunctionWithTrailingClosure")
}
}
func anotherFunctionWithTrailingClosure(closure: #escaping () -> ()) {
print("starting anotherFunctionWithTrailingClosure")
_anotherFunctionWithTrailingClosure = closure
DispatchQueue.global().asyncAfter(deadline: .now() + 1) { [weak self] in
self?._anotherFunctionWithTrailingClosure!()
print("finishing anotherFunctionWithTrailingClosure")
}
}
func doSomething() {
print("doSomething")
}
public func testCompletionHandlers() {
someFunctionWithTrailingClosure { [weak self] in
guard let self = self else { return }
self.anotherFunctionWithTrailingClosure { // [weak self] in
self.doSomething()
}
}
}
// go ahead and add `deinit`, so I can see when this is deallocated
deinit {
print("deinit")
}
}
func performExperiment() {
let obj = CaptureListExperiment()
obj.testCompletionHandlers()
Thread.sleep(forTimeInterval: 1.3)
}
performExperiment()
/* Output:
starting someFunctionWithTrailingClosure
starting anotherFunctionWithTrailingClosure
finishing someFunctionWithTrailingClosure
doSomething
finishing anotherFunctionWithTrailingClosure
*/
Notice that deinit is not called, since a retain cycle was created.
This could be fixed by either removing the strong reference (EX2):
public func testCompletionHandlers() {
someFunctionWithTrailingClosure { [weak self] in
//guard let self = self else { return }
self?.anotherFunctionWithTrailingClosure { // [weak self] in
self?.doSomething()
}
}
}
Or using the weak/strong/weak dance (EX3):
public func testCompletionHandlers() {
someFunctionWithTrailingClosure { [weak self] in
guard let self = self else { return }
self.anotherFunctionWithTrailingClosure { [weak self] in
self?.doSomething()
}
}
}
Updated for Swift 4.2:
public class CaptureListExperiment {
public init() {
}
func someFunctionWithTrailingClosure(closure: #escaping () -> ()) {
print("starting someFunctionWithTrailingClosure")
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
closure()
print("finishing someFunctionWithTrailingClosure")
}
}
func anotherFunctionWithTrailingClosure(closure: #escaping () -> ()) {
print("starting anotherFunctionWithTrailingClosure")
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
closure()
print("finishing anotherFunctionWithTrailingClosure")
}
}
func doSomething() {
print("doSomething")
}
public func testCompletionHandlers() {
someFunctionWithTrailingClosure { [weak self] in
guard let self = self else { return }
self.anotherFunctionWithTrailingClosure { // [weak self] in
self.doSomething()
}
}
}
// go ahead and add `deinit`, so I can see when this is deallocated
deinit {
print("deinit")
}
}
try it Playgorund:
func performExperiment() {
let obj = CaptureListExperiment()
obj.testCompletionHandlers()
Thread.sleep(forTimeInterval: 1.3)
}
performExperiment()