Delegate pattern – ARC – Object not deallocated (when using managed objects)

Home / ios / Delegate pattern – ARC – Object not deallocated (when using managed objects)

Question:
I’m having trouble with the Automatic Reference Counting in swift. This is my setup:
I have a UINavigationController where I pushed MyViewController (see below). This view controller uses a DataProvider object that provides data from CoreData, tracks changes and notifies the delegate (in this case MyViewController).

Now, when I pop MyViewController from the navigation controller, I expect MyViewController and DataProvider to deallocate (or deinitialize, don’t know which term is correct). But it’s not happening (it should print "deinit …").

After searching the internet for quite a while, I’m pretty confident to say it’s caused by the ARC. I guess there is a strong reference somewhere that’s blocking the deallocation. But where, and how do I fix it?

First, I thought it is caused the managedObjectContext property of DataProvider (see (1) below). I added a unowned to fix it. Though I’m not 100% certain why, it does work.

But it only works in some Provider classes, that do not have properties like person: Person (see (2) below). So with DataProvider it does not work, they do not deallocate.

I tried adding unowned there as well, but it’s not working. It rather sometimes crashes (I don’t quite know the cause). I also tried to allocate person inside MyViewController instead of doing it inside of DataProvider. Again, that didn’t change anything.

I’m not that good at retain cycles, do you have any idea how to solve this? Tell me, if you need more info, then I’m gonna edit the question.
class MyViewController: UIViewController {
var personManagedObjectID: NSManagedObjectID!
var dataProvider: DataProvider!
var mainViewContext: NSManagedContext!

override func viewDidLoad() {
super.viewDidLoad()
dataProvider = DataProvider(forPersonWithObjectID: personManagedObjectID, using: mainViewContext)
dataProvider.delegate = self
….
}

deinit {
print("deinit MyViewController")
}


}

class DataProvider: NSObject {
// (1)
unowned private var managedObjectContext: NSManagedObjectContext
weak public var delegate: ProvidersDelegate?

// (2)
unowned public var person: Person

init(forPersonWithObjectID objectID: NSManagedObjectID, using managedObjectContext: NSManagedObjectContext) {
self.managedObjectContext = managedObjectContext
self.person = managedObjectContext.object(with: objectID) as! Person
super.init()

NotificationCenter.default.addObserver(self, selector: #selector(objectHasChanged(notif:)), name: Notification.Name.NSManagedObjectContextObjectsDidChange, object: managedObjectContext)
}

@objc private func objectHasChanged(notif: Notification) {
delegate?.someMethod(…)
}

deinit {
print("deinit DataProvider")
}


}


Answer:
Make dataProvider in your MyViewController optional and in viewWillDisapear set it nil, in your DataProvider class make managedObjectContext object optional and in deinit method set it nil (also remove notification observers of DataProvider)
Read more

Leave a Reply

Your email address will not be published. Required fields are marked *