Swift: Assign a class method to a handler and handling weak self - swift

I just went memory-leak hunting in the app I am working on, and noticed that the following produces a memory leak:
class SubClass {
var didCloseHandler: (() -> Void)?
}
class MainClass {
var subClass = SubClass()
func setup {
subClass.didCloseHandler = self.didCloseSubClass
}
func didCloseSubClass() {
//
}
}
This produces a retain cycle, and for good reason - didCloseHandler captures MainClass strongly, and MainClass captures SubClass strongly.
My Question: Is there a way in Swift that allows me to assign a class method to a handler without a retain cycle?
And yes, I am aware that I can do this using subClass.didCloseHandler = { [weak self] self?.didCloseSubClass() }. I'm wondering, though, if it can be done without introducing a new closure.

make a weak reference of subClass in MainClass

If you don't have strong reference to SubClass instance somewhere else - you may try wrapper like this:
func WeakPointer<T: AnyObject>(_ object: T, _ method: #escaping (T) -> () -> Void) -> (() -> Void) {
return { [weak object] in
method(object!)()
}
}
Then use it like this:
func setup() {
subClass.didCloseHandler = WeakPointer(self, MainClass.didCloseSubClass)
}
If you don't need properties from MainClass instance in didCloseSubClass implementation - you can make this method static, which will also solve your problem.
If you have strong reference to SubClass instance somewhere else and it won't be deallocated immediately - weak var subClass will do, as was already mentioned.
EDIT:
I've come up with another idea. It may look a bit more complicated, but maybe it would help.
import Foundation
class SubClass {
#objc dynamic func didCloseHandler() {
print(#function)
}
deinit {
print(" \(self) deinit")
}
}
class MainClass {
var subClass = SubClass()
func setup() {
if let implementation = class_getMethodImplementation(MainClass.self, #selector(didCloseSubClass)),
let method = class_getInstanceMethod(SubClass.self, #selector(SubClass.didCloseHandler)) {
method_setImplementation(method, implementation)
}
}
#objc func didCloseSubClass() {
print(#function)
}
deinit {
print(" \(self) deinit")
}
}
You change closure for #objc dynamic method and set it's implementation to the one from MainClass in setup().

Related

Swift protocol on class instance never fires

I have a swift protocol, but it never fires.
I have 1 class which is an instance, and the other is a class where I want to manage an object;
protocol TurnDelegate: class {
func turnIsCompleted()
}
class ClassOne : NSObject {
weak var delegate: TurnDelegate?
override init() {
super.init()
delegate?.turnIsCompleted()
}
}
class ClassTwo: NSObject, TurnDelegate {
static var instance = ClassTwo()
func turnIsCompleted() {
print ("Turn is completed")
}
}
let c2:ClassTwo = ClassTwo.instance
let c1:ClassOne = ClassOne.init()
My issue is that the protocol never fires and does not output "turn is completed"
How can I resolve this?
Edit: How do I set the delegate?
Many thanks
In case you have describe create custom init.
class ClassOne : NSObject {
weak var delegate: TurnDelegate?
init(with delegate: TurnDelegate?) {
self.delegate = delegate
delegate?.turnIsCompleted()
}
}
Than:
let c2:ClassTwo = ClassTwo.instance
let c1:ClassOne = ClassOne.init(with: c2)
Output:
Turn is completed
You forgot to set the delegate.
Usually the delegate is set in an init method. The method in the protocol is called later in another method for example
protocol TurnDelegate: class {
func turnIsCompleted()
}
class ClassOne : NSObject {
weak var delegate: TurnDelegate?
init(delegate: TurnDelegate?) {
self.delegate = delegate
}
func turnSomething()
{
delegate?.turnIsCompleted()
}
}
class ClassTwo: NSObject, TurnDelegate {
static let instance = ClassTwo()
func turnIsCompleted() {
print ("Turn is completed")
}
}
let c2 = ClassTwo.instance
let c1 = ClassOne(delegate: c2)
c1.turnSomething()
However for this purpose especially in conjunction with a singleton I'd prefer a callback closure rather than protocol / delegate. The benefit is less overhead and the callback is directly connected to the calling method.
class ClassOne : NSObject {
func turnSomething()
{
let c2 = ClassTwo.instance
c2.turn {
print ("Turn is completed")
}
}
}
class ClassTwo: NSObject {
static let instance = ClassTwo()
func turn(completion: ()->()) {
// do heavy work
completion()
}
}
let c1 = ClassOne()
c1.turnSomething()
Delegates in all their glory do have their drawbacks too. One of them is that relationships between objects and their delegates have to be established explicitly. In Cocoa there are typically two ways of doing this. One is connecting a delegate IBOutlet in InterfaceBuilder, the other is doing it programmatically. As #OlegGordiichuck points out you could do it in the initializer, but generally in Cocoa delegates tend to be properties. In your case this would boil down to instantiate objects of ClassTwo and ClassOne and then manually set the delegate of c2 as in
c2.delegate = c1
This however defeats your notification mechanism and you would have to have a separate method for notifying the delegate (Which is again typical, as usually your delegate cannot know about is significant other during its construction. Moreover the construction of the originator is usually not something the delegate would have to know about).

Testing Delegation in Playground giving 'nil'

I have the following code in Playground -I'm learning delegation-...
import UIKit
protocol FollowThisProtocol {
func passingTheValue(aValue: String)
}
class IPassTheValues{
var aDelegate: FollowThisProtocol!
func runThisFunc(){
aDelegate.passingTheValue(aValue: "I like this game")
}
}
class IReceiveTheValues: FollowThisProtocol{
var localString: String!
var instanceOfClass: IPassTheValues!
func runReceivefunc(){
instanceOfClass.aDelegate = self
}
func passingTheValue(aValue: String) {
localString = aValue
}
}
When I attempt to
print(IReceiveTheValues().localString)
it's giving me nil
It also gives me nil if I run the following lines before attempting to print(IReceiveTheValues().localString)...
IPassTheValues()
IReceiveTheValues()
could you please help me understand why the value is not being passed from the 1st class to the 2nd..?
Or if you can spot something in my code that is contradicting itself, could you please point it out..?
Appreciate your time and help.
You need to create the IPassTheValues object before assigning yourself as the delegate, and then call runThisFunc() on the instance:
func runReceivefunc(){
instanceOfClass = IPassTheValues()
instanceOfClass.aDelegate = self
instanceOfClass.runThisFunc()
}
Then test:
// Create the `IReceiveTheValues` object
let irtv = IReceiveTheValues()
// Run the method
irtv.runReceivefunc()
// Get the resulting string
print(irtv.localString)
I suggest 2 other changes. Make your delegate weak so that you don't get a retain cycle which makes it impossible to delete either object. In order to do that, you will need to add : class to your protocol declaration because only reference objects (instances of a class) can be weak.
Here's the modified code. Try it and see what happens when you delete weak.
protocol FollowThisProtocol: class {
func passingTheValue(aValue: String)
}
class IPassTheValues{
weak var aDelegate: FollowThisProtocol!
func runThisFunc(){
print("Calling delegate...")
aDelegate.passingTheValue(aValue: "I like this game")
}
deinit {
print("IPassTheValues deinitialized")
}
}
class IReceiveTheValues: FollowThisProtocol{
var localString: String!
var instanceOfClass: IPassTheValues!
func runReceivefunc(){
instanceOfClass = IPassTheValues()
instanceOfClass.aDelegate = self
instanceOfClass.runThisFunc()
}
func passingTheValue(aValue: String) {
print("Receiving value from helper object...")
localString = aValue
}
deinit {
print("IReceiveTheValues deinitialized")
}
}
func test() {
let irtv = IReceiveTheValues()
irtv.runReceivefunc()
print(irtv.localString)
}
test()

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 */ })

How to tell if a subclass overrides a method in Swift

I’ve got a Swift class Parent that has a method doSomething() and I want to detect (in Parent) if a subclass has overridden doSomething(). How do I do that?
class Parent {
func doSomething() {}
func subclassOverridesDoSomething() -> Bool {
// what goes here?
return true
}
}
class Child: Parent {
override func doSomething() {}
}
I know I can do this with NSObject or even the Objective C runtime functions, but how can I do it with Swift classes?
You can use the Objective-C runtime provided you expose the function with dynamic:
class Parent {
func doSomething() {}
func subclassOverridesDoSomething(t:Parent.Type) -> Bool {
let originalMethod = class_getInstanceMethod(t, "doSomething")
return originalMethod != nil
}
}
class Child: Parent {
dynamic override func doSomething() {}
}
Parent().subclassOverridesDoSomething(Child.self)
If you omit dynamic, it won't work because Objective-C can't see the method.
Well Swift doesn't really offer methods to do that.
Also in my opinion, there is no disadvantage in using Objective-c methods like method_getImplementation or the NSObject. You will have to use the methods objective-c offers you to solve your problem.
For example:
let selector = Selector("viewWillAppear:")
let originalMethod = class_getInstanceMethod(YourClass, selector)

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.