Swift weak self function retention - swift

If I have a closure that references a weak var weakSelf = self, can I change that closure to a direct function reference, through weakSelf?
struct ClosureHolder {
let closure: () -> Void
}
class ClosureSource {
func hello() {
NSLog("hello")
}
func createWeakSelfWithinInnerClosureClosureHolder() -> ClosureHolder {
weak var weakSelf = self
return ClosureHolder(closure: {
weakSelf?.hello()
})
}
func createWeakSelfDotHelloClosureHolder() -> ClosureHolder {
weak var weakSelf = self
// The code below won't compile because weakSelf is an Optional.
// Once I unwrap the optional, I no longer have a weak reference.
// return ClosureHolder(closure: weakSelf.hello)
// this strongifies the weak reference. :(
return ClosureHolder(closure: weakSelf!.hello)
}
}
Instead of createWeakSelfWithinInnerClosureClosureHolder, I'd prefer something like createWeakSelfDotHelloClosureHolder.

No you can't. Saying self.foo (if foo is a method) is exactly the same thing as to saying MyClass.foo(self). And methods curried in this fashion always keep a strong reference to the receiver object. If you want to maintain a weak reference, then you need to stick with the { weakSelf?.hello() } approach.

Related

Weird weak self and retain cycle behaviour

Let's consider following code:
// Just for easier testing
protocol Printer {
var delayer: Delayer { get }
}
// Retain cycle
class Printer1: Printer {
private func action() {
print("action")
}
private(set) lazy var delayer: Delayer = {
return Delayer(action)
}()
deinit {
print("deinit")
}
}
// Works fine, but weak mess
class Printer2: Printer {
private func action() {
print("action")
}
private(set) lazy var delayer: Delayer = {
return Delayer { [weak self] in self?.action() }
}()
deinit {
print("deinit")
}
}
// Questionable hack, but works fine
class Printer3: Printer {
private func action() {
print("action")
}
private(set) lazy var delayer: Delayer = {
return Delayer(weakAction)
}()
// computed property or function is also fine here
private lazy var weakAction: () -> Void = {
return { [weak self] in
self?.action()
}
}()
deinit {
print("deinit")
}
}
// Retain cycle
class Printer4: Printer {
private func action() {
print("action")
}
private(set) lazy var delayer: Delayer = {
weak var welf: Printer4? = self
return Delayer(welf?.action ?? {})
}()
deinit {
print("deinit")
}
}
// Works fine
class Printer5: Printer {
private func action() {
print("action")
}
private(set) lazy var delayer: Delayer = {
weak var welf: Printer5? = self
return Delayer { welf?.action() }
}()
deinit {
print("deinit")
}
}
class Delayer {
private var action: () -> Void
init(_ action: #escaping () -> Void) {
self.action = action
}
func run() {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in
self?.action()
}
}
}
So we have a Printer class which contains a Delayer class that takes the action on Printer and performs it delayed.
We call it something like this:
var printer: Printer? = PrinterX()
printer?.delayer.run()
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
printer = nil
}
It is clear why Printer1 creates retain cycle. Action is passed into delayer with implicit strong self which cannot be released because Delayer is owned by Printer.
Printer2 is the intended way in my opinion. Obviously doesn't create retain cycle, but it is kind of mess to write all the time. Thats why I started experimenting with other solution.
I don't understand why Printer3 doesn't create retain cycle. Because weakAction is property on self, so passing it like that into Delayer should create strong reference like in Printer1.
I also don't understand why Priner4 does create retain cycle. welf is local weak reference to self, so it should not increase the reference count when passing it into the Delayer.
Strangely enough using the welf inside closure in Printer5 doesn't create retain cycle.
Questions
Can anyone please explain to me this weird behavior on Printer3, Printer4 and Printer5
I am tempted to use the Printer3 solution. Is it safe to use? As it seems almost like a bug, can I use it without worrying about it being fixed in future versions and therefore creating retain cycle in my app?
First of all, all printers are creating and retaining their own Delayer. The delayer takes a closure and, in turn, retains that closure.
Let's try to walk through them one by one.
Printer1
As you stated yourself, it's pretty clear why it's creating a retain cycle. You are passing the self.action instance method as the closure to the Delayer, and since all closures are reference types, passing self.action will retain its surrounding scope (which is Printer1).
Printer2
Again, pretty obvious here. You're explicitly capturing a weak reference to self inside the closure you're passing to Delayer, hence not creating a retain cycle.
Printer3
Here, a retain cycle is not created, because the self.weakAction property is called immediately, and its result (a closure which holds a weak reference to self) is passed on to Delayer. This, in effect, is the exact same thing as what's happening in Printer2.
Printer4
First, you're capturing a weak reference to self, and then fetching welf?.action and passing the result into Delayer. Again, welf?.action is called immediately, and the result (a pointer to an instance method) is passed on to Delayer. The weak reference to self is only kept for the duration of the surrounding scope (the lazy var creation scope), and passing the action instance method will retain self. This is identical to Printer1.
Printer5
Here, you're first creating a weak reference to self, and then you're capturing that weak reference inside a new closure that is passed to Delayer. Since self is never directly referenced in the passed closure, it will not capture self in that scope, only the welf weak reference. This is pretty much identical to Printer2, but with a slightly different syntax.
Personally, I would opt for the Printer2 way (creating a new closure, retaining a weak reference to self and using that to call self?.action). It makes for the easiest code to follow (as opposed to retaining a variable with a closure that weakly captures self). But, depending on what you're actual use case is, it might of course make sense.

didSet for weak reference not working as expected

I have this small Swift script, which uses weak references:
#!/usr/bin/env swift
class Thing
{
deinit
{
print("Thing object deallocated")
}
}
class WeakThing
{
weak var thing: Thing?
{
didSet
{
print("Set thing to \(thing)")
}
}
}
var thing = Thing()
let weakThing = WeakThing()
weakThing.thing = thing
thing = Thing()
print("weakThing's thing is \(weakThing.thing)")
This prints:
Set thing to Optional(Test.Thing)
Thing object deallocated
weakThing's thing is nil
However, I would expect it to print:
Set thing to Optional(Test.Thing)
Set thing to nil
Thing object deallocated
weakThing's thing is nil
What am I doing incorrectly? I see that the object is being deallocated, and that the value of the thing variable is changing, but my didSet code is not executing.
didSet and willSet are not called when a weak-reference is auto-zeroed due to ARC.
If you were to manually set the property to nil, you would see the didSet code called.
I know this question is very old, but I stumbled across another answer that actually get's the problem solved here: https://stackoverflow.com/a/19344475/4069976
For what it's worth, this is my implementation to watch a deinit as suggested by the answer referenced above. Just make sure you don't create any retain cycles with your onDeinit closure!
private var key: UInt8 = 0
class WeakWatcher {
private var onDeinit: () -> ()
init(onDeinit: #escaping () -> ()) {
self.onDeinit = onDeinit
}
static func watch(_ obj: Any, onDeinit: #escaping () -> ()) {
watch(obj, key: &key, onDeinit: onDeinit)
}
static func watch(_ obj: Any, key: UnsafeRawPointer, onDeinit: #escaping () -> ()) {
objc_setAssociatedObject(obj, key, WeakWatcher(onDeinit: onDeinit), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
deinit {
self.onDeinit()
}
}
Call it like this when initializing your weak var:
self.weakVar = obj
WeakWatcher.watch(obj, onDeinit: { /* do something */ })

Swift using function parameter inside closure

I just want to use function parameter inside swift closure without memory leak, so I just want to confirm if I do in following way will there be any memory related issues? Kindly let me know
func someMethod(someValue: String) {
weak var weakSelf = self
var copyOfSomeValue: String? = someValue.copy() as? String
self.someOtherMethodWithCompletion(completionHandler: { () -> Void in
if let strongSelf = weakSelf, let originalValue = copyOfSomeValue {
strongSelf.updateMyViewWithText(originalValue)
}
})
}
String is a value type. Even though closures capture them by reference, there is no need to make a copy unless you are going to change someValue after the capture. Even then you're better off using capture lists [someValue], which are also used when you need to declare [weak self].
Using weak or unowned is situational, read about them here.
func someMethod(someValue: String) {
someOtherMethodWithCompletion { [weak self] in
self?.updateMyViewWithText(someValue)
}
}
With swift you should use [unowned self] and just use swift as normal, for example:
func someMethod(someValue: String) {
var copyOfSomeValue: String? = someValue.copy() as? String
self.someOtherMethodWithCompletion(completionHandler: { () ->
[unowned self]
in
if let originalValue = copyOfSomeValue {
self.updateMyViewWithText(originalValue)
}
})
}
You should use weak or unowned just to variables which can cause reference cycle. The difference between those is:
Use a weak reference whenever it is valid for that reference to become
nil at some point during its lifetime. Conversely, use an unowned
reference when you know that the reference will never be nil once it
has been set during initialization

weak to non class types to avoid memory leak

I've got a memory leak in this case, if I pass a reference to any method, the self comes with it which increases it's reference count I guess, how can I make non class types to be weak
public class Observer {
weak private var method: ((message: AnyObject) -> ())! //error here
weak private var context: AnyObject!
}
public init(method: (AnyObject -> ())?, context: AnyObject?) {
self.method = method
self.context = context
}
in another class I guess self.callback creates a strong reference to the caller object and passes on.
var observer = Observer(method: self.callback, context: self) //pass of self.callback is a strong reference
Edit:
Working on the above, my attempt using an example that further clarifies the situation using two classes. deinit never gets called.
class Test {
private var ref: Observer?
init() {
ref = Observer(method: self.callback, context: self)
}
func callback(message: AnyObject) {
}
deinit {
println("deinit test")
}
}
public class Observer {
private var method: ((message: AnyObject) -> ())?
weak private var context: AnyObject!
public init(method: (AnyObject -> ())?, context: AnyObject?) {
self.method = method
self.context = context
}
deinit {
println("deinit observer")
}
}
From looking at your code, it seems like you are talking about a retain cycle where the Test object holds onto the Observer object through the variable ref, the Observer object holds onto the closure formed by doing self.callback, which holds onto self.
Generally in such cases, you don't want the closure property itself to be weak. Rather, you want the closure to capture a weak reference to self (the Test object is passing a "callback" to itself to another object). However, that is somewhat confusing here as we are not explicitly using closure syntax (rather, you are getting a closure by accessing a method on an instance and not calling it). The problem of capturing a weak reference to self in this case was covered in this question.
The best solution is:
ref = Observer(method: {[unowned self] in self.callback($0)}, context: self)
Try this:
public class Observer {
private var method: ((message: AnyObject) -> ())?
weak private var context: AnyObject!
public init(method: (AnyObject -> ())?, context: AnyObject?) {
self.method = method
self.context = context
}
}
I tried it and it doesn't create a strong reference cycle. But I also tried with ! instead of ?, and that didn't caused as well, and I hope somebody is out there to explain that.

Swift: Unable to access weak ref to func()

I'm getting the following error:
~.swift:89:50: 'TreeContainerView?' does not have a member named
'updateValues'
..even though 'updateValues' does exist as shown below.
class TreeContainerView:UIView {
override func awakeFromNib() {
weak var weakSelf:TreeContainerView? = self
let delayInSeconds:Double = 0.25
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
...
dispatch_source_set_event_handler(timer, weakSelf.updateValues);
dispatch_resume(timer);
}
func updateValues() {
}
...
}
However it does compile if I replace 'weakSelf.updateValues' with 'self.updateValues'.
Question:
1) Is the following the correct Swift version (the ObjC uses a block)?
dispatch_source_set_event_handler(timer, weakSelf.updateValues);
2) How do I implement a weak reference in Swift (per this context)?
The issue is that weakSelf is an optional TreeContainerView. In order to access its methods you have unwrap weak self to access the actual TreeContainerView if it exists like this:
dispatch_source_set_event_handler(timer, weakSelf!.updateValues);