Many posts seem to advise against notifications when trying to synchronize functions, but there are also other posts which caution against closure callbacks because of the potential to inadvertently retain objects and cause memory issues.
Assume inside a custom view controller is a function, foo
, that uses the Bar
class to get data from the server.
class CustomViewController : UIViewController {
function foo() {
// Do other stuff
// Use Bar to get data from server
Bar.getServerData()
}
}
Option 1: Define getServerData
to accept a callback. Define the callback as a closure inside CustomViewController
.
Option 2: Use NSNotifications
instead of a callback. Inside of getServerData
, post a NSNotification
when the server returns data, and ensure CustomViewController
is registered for the notification.
Option 1 seems desirable for all the reasons people caution against NSNotification
(e.g., compiler checks, traceability), but doesn't using a callback create a potential issue where CustomViewController
is unnecessarily retained and therefore potentially creating memory issues?
If so, is the right way to mitigate the risk by using a callback, but not using a closure? In other words, define a function inside CustomViewController
with a signature matching the getServerData
callback, and pass the pointer to this function to getServerData
?
I'm always going with Option 1 you just need to remember of using [weak self]
or whatever you need to 'weakify' in order to avoid memory problems.
Real world example:
filterRepository.getFiltersForType(filterType) { [weak self] (categories) in
guard let strongSelf = self, categories = categories else { return }
strongSelf.dataSource = categories
strongSelf.filteredDataSource = strongSelf.dataSource
strongSelf.tableView?.reloadData()
}
So in this example you can see that I pass reference to self
to the completion closure, but as weak
reference. Then I'm checking if the object still exists - if it wasn't released already, using guard
statement and unwrapping weak
value.
Definition of network call with completion closure:
class func getFiltersForType(type: FilterType, callback: ([FilterCategory]?) -> ()) {
connection.getFiltersCategories(type.id).response { (json, error) in
if let data = json {
callback(data.arrayValue.map { FilterCategory(attributes: $0) } )
} else {
callback(nil)
}
}
}