Consider the following, simplified structure:
class MyClass {
weak var delegate: MyClassDelegate?
}
protocol MyClassDelegate: class {
func goneWrong()
}
And the test case that throws the EXC_BAD_ACCESS (0x40dedeadbec0) error:
class MyTest: XCTestCase {
func test() {
let exp = expectation(description: "Expecting a call")
let a = MyClass()
a.delegate = MyMockDelegate(exp: exp)
// Thread 1: EXC_BAD_ACCESS(code=1, address=0x40dedeadbec0)
waitForExpectations(timeout: 10)
}
class MyMockDelegate: MyClassDelegate {
let exp: XCTestExpectation
init(exp: XCTestExpectation) {
self.exp = exp
}
func goneWrong() {
self.exp.fulfill()
}
}
}
The error is not thrown if the delegate var is declared normally, without the weak keyword. The problem is that it needs to be weak to avoid the strong reference cycle in the real code (not the test case). How do I go around this?
This is an old question, so I'm sure you solved it by now, but for the sake of future readers, the problem is that you've defined your delegate to be weak (i.e. allow it to be set to nil and the object deallocated as soon as their are no strong references). But you have no strong references to your MyMockDelegate instance:
let a = MyClass()
a.delegate = MyMockDelegate(exp: exp)
Now, I'd expect it to gracefully nil the delegate reference in this scenario, and I don't know why you got error at 0x40dedeadbec0 address, because I don't. (BTW, I don't think 0x40dedeadbec0 is a real memory address, but rather is one of those bad puns by some kernel developer.)
Anyway, if you expect your mock delegate to work, you need to keep a reference to it until the end of your test, e.g.:
class MyAppTests: XCTestCase {
func test() {
let exp = expectation(description: "Expecting a call")
var delegate: MyMockDelegate? = MyMockDelegate(exp: exp)
let a = MyClass()
a.delegate = delegate
// do something
waitForExpectations(timeout: 10)
delegate = nil
}
}
Alternatively, you can create a property in your test class to keep a strong reference to the delegate for the life of the test. Either way, you need more than a single weak reference if you don't want that disappearing on you.
Related
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.
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().
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).
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()
This question already has an answer here:
How to call deinit in Swift [duplicate]
(1 answer)
Closed 5 years ago.
I came across something that's peculiar and interesting and would love to get inputs from anyone. So to start off with lets take this definition of the class:
class TestClass:NSObject {
var s1 = NSHashTable<TestClass>(options: .weakMemory)
func doit(bla:TestClass) {
s1.add(bla)
bla.s1.add(self)
}
deinit {
print("Deinit")
}
}
Now lets consider the following:
var t1:TestClass? = TestClass()
var t2:TestClass? = TestClass()
If we did the following deinit gets called:
t1?.s1.add(t2!)
t2?.s1.add(t1!)
t1 = nil // This will result in deinit being called
Now lets do the same thing but by calling doit() method
t1?.doit(bla:t2!)
t1 = nil // Deinit doesn't get called for some reason
The question here is why isn't deinit being called in this situation? What is so different about this since it essentially has the same reference assignment as the first method?
I would love to get input from anyone on this.
As usual, the problem is that you are trying to test this in a playground. Don't. Playgrounds are the work of the devil.
Test in an actual app project and you will see that deinit is called.
Example (iOS, but the equivalent in macOS would do fine):
import UIKit
class TestClass:NSObject {
var s1 = NSHashTable<TestClass>(options: .weakMemory)
func doit(bla:TestClass) {
s1.add(bla)
bla.s1.add(self)
}
deinit {
print("Deinit")
}
}
class ViewController: UIViewController {
var t1:TestClass? = TestClass()
var t2:TestClass? = TestClass()
override func viewDidLoad() {
super.viewDidLoad()
t1?.doit(bla:t2!)
t1 = nil // --> "Deinit"
print(t2?.s1) // --> it's empty
}
}
deinit is not called because you've created reference cycle.
First you're creating sctrong reference from self to bla: s1.add(bla)
Second you create strong reference from bla to self: bla.s1.add(self)
And now they both have references to each other, so they won't deinit if you just nullify one of them.
I've modified your TestClass to remove reference cycle:
class TestClass:NSObject {
weak var s1 = NSHashTable<TestClass>(options: .weakMemory)
func doit(bla:TestClass) {
s1?.add(bla)
bla.s1?.add(self)
}
deinit {
print("Deinit")
}
}
Now your second call will trigger deinit properly.