I had set my delegate to self sometimes, just sometimes, the delegate won't set. It is nil. It seems like some memory management issue. I want to know is there any solution to solve this without any memory leak? I will copy my code and my debugging steps in here.
func openConversationForPost(_ post: PostViewStructure, viewController: UIViewController) {
let dest = ChatViewController(service: service, conversationID: post.token!)
dest.delegate = self
viewController.navigationController?.pushViewController(dest, animated: true)
}
ps1. I know I can use strong but then there will be high risk of crashing.
ps2. The weird thing is when I set the break right on the line where the delegate is being set there is no problem; but, if I let the flow of the programming running normally the delegate is nil.
Edit
The init func of ChatViewController
init(service: ServicesProtocol, conversationID: String) {
self.conversationID = conversationID
super.init(service: service)
defer { conversation = DataProvider().object(ChatConversation.self, key: conversationID) }
}
Related
I have a question about memory management in iOS.
As I know we have ARC that looks at a number of strong references to the object and once it's become 0 object will be deallocated.
Here is a code sample that makes me confused
final class SecondVC: UIViewController {
let titleLabel: UILabel = UILabel()
let network = Network()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .red
self.view.addSubview(titleLabel)
network.makeCall {
self.titleLabel.text = "Title"
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.navigationController?.popViewController(animated: true)
}
deinit {
print("SecondVC deinit called")
}
}
final class Network {
func makeCall(completion: #escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
}
}
deinit {
print("Network deinit called")
}
}
As I understand VC holding network with a strong reference.
At the same time network capture VC with a strong reference.
The completion callback was never called. So the number of references shouldn't go to 0.
But deinit called for both objects.
The questin is
Is there a memory leak? And why deinit is called if in theory we have object that should not be deallocated.
UPD
So, the answer is.
No one captures completion. Completion is released after makeCall and self captured by it is also released.
As you are using #escaping in your closure there is nothing hanging on to the reference so the count can be reset
here's a good article on #escaping
https://www.donnywals.com/what-is-escaping-in-swift/
** Edit,
Sorry this was poorly worded - "in your closure there is nothing hanging on to the reference" was the key bit from this as the closure is never called - the escaping part was confusing things as its really unrelated to the OP's question, sorry for the confusion
I'm new to Swift and I'm having a hard time understanding the purpose of assigning self to a delegate. Part of the difficulty stems from the fact that delegate seems to be used in two different ways.
First is as means to send messages from one class to another when a specific event happens, almost like state management. Second is to enable "a class or structure to hand off (or delegate) some of its responsibilities to an instance of another type," as stated in documentation. I have a feeling that these two are fundamentally the same and I'm just not getting it.
protocol PersonProtocol {
func getName() -> String
func getAge() -> Int
}
class Person {
var delegate: PersonProtocol?
func printName() {
if let del = delegate {
print(del.getName())
} else {
print("The delegate property is not set")
}
}
func printAge() {
if let del = delegate {
print(del.getAge())
} else {
print("The delegate property is not set")
}
}
}
class ViewController: UIViewController, PersonProtocol {
var person: Person!
override func viewDidLoad() {
person.delegate = self
person.printAge()
person.printName()
}
func getAge() -> Int {
print("view controller")
return 99
}
func getName() -> String {
return "Some name"
}
}
What is the purpose of person.delegate = self in this case? Isn't ViewController already required to conform to PersonProtocol without it?
I have a feeling that these two are fundamentally the same
The first is a special case of the second. "send messages from one class to another" is just a specific way of "handing off some of its responsibilities". The "messages" are the "responsibilities"
What is the purpose of person.delegate = self in this case?
Here, person delegates (i.e. hands off) some of its responsibilities to another object. It does this by sending messages to another object. First, it needs to identify which objects it can delegate these responsibilities to. This is achieved by requiring that its delegate conform to PersonProtocol, as PersonProtocol defines the messages that Person is going to send.
Next, person needs to know exactly which object it should send these messages to. This is what person.delegate = self does. Remember that person doesn't know anything about your ViewController until this point. Instead of = self, you could say:
person.delegate = SomeOtherClassThatConformsToPersonProtocol()
and person will send its messages to that object instead, and the methods in your ViewController won't be called.
Isn't ViewController already required to conform to PersonProtocol without it?
Correct, but without it, person doesn't know which object it should send its messages to, and as a result, the methods in your ViewController won't be called.
Note that the delegate property should be declared as weak to avoid retain cycles. When you do person.delegate = self, you get a retain cycle: self has a strong reference to person, person also has a strong reference to self via the delegate property.
If you notice inside your Person class, delegate is nil. If you don't execute person.delegate = self, delegate will remain nil.
In other words, assigning ViewController to person.delegate allows Person to identify who the delegate is (i.e., have a reference to ViewController), and that way you can successfully execute statements like delegate?.getName() or delegate?.getAge() from the Person class.
that means Person is not able to getName() and getAge() so Person class delegate that to other DataSource.
Lets say the your view controller has a data source class PersonDataSource which deal with API to get this information So
class PersonDataSource: PersonProtocol {
func getAge() -> Int {
print("view controller")
return 99
}
func getName() -> String {
return "Some name"
}
}
so the view controller will looks like this
class ViewController: UIViewController {
var person: Person!
var personDataSource = PersonDataSource()
override func viewDidLoad() {
person.delegate = personDataSource
person.printAge()
person.printName()
}
}
I am building a new app using MVVM + Coordinators. Specifically, I am using the Coordinator pattern found on https://github.com/daveneff/Coordinator.
On the top level I have an AppCoordinator that can start the child coordinator RegisterCoordinator. When the sign up flow is complete, the AppCoordinator then switches the root viewcontroller of its navigator, and the coordinators and viewcontrollers used in the sign up flow should be released from memory.
final class AppCoordinator: CoordinatorNavigable {
var dependencies: AppDependencies
var childCoordinators: [Coordinator] = []
var rootViewController = UINavigationController()
var navigator: NavigatorType
init(window: UIWindow, dependencies: AppDependencies) {
self.dependencies = dependencies
navigator = Navigator(navigationController: rootViewController)
dependencies.userManager.delegate = self
window.rootViewController = rootViewController
window.makeKeyAndVisible()
}
func start() {
if dependencies.properties[.user] == nil {
// Logged out state
let vc = AuthFlowViewController.instantiate(storyboardName: Constants.Storyboards.authFlow)
vc.delegate = self
navigator.setRootViewController(vc, animated: false)
} else {
// Logged in
let vc = HomeViewController.instantiate(storyboardName: Constants.Storyboards.home)
vc.viewModel = HomeViewModel(dependencies: dependencies)
navigator.setRootViewController(vc, animated: false)
}
childCoordinators = []
}
}
extension AppCoordinator: UserManagerDelegate {
func authStateChanged() {
// User logged in or logged out; show the correct root view controller
start()
}
func userChanged() {}
}
extension AppCoordinator: AuthFlowViewControllerDelegate {
func login() {
dependencies.userManager.changeUser(newUser: User(id: 1, name: "Kevin"))
}
func startRegisterFlow() {
let registerCoordinator = RegisterCoordinator(dependencies: dependencies, navigator: navigator)
pushCoordinator(registerCoordinator, animated: true)
}
}
The RegisterCoordinator meanwhile simply pushes multiple viewcontrollers onto the navigator's stack:
class RegisterCoordinator: CoordinatorNavigable {
var dependencies: AppDependencies
var childCoordinators: [Coordinator] = []
var navigator: NavigatorType
let rootViewController = PhoneInputViewController.instantiate(storyboardName: Constants.Storyboards.authFlow)
init(dependencies: AppDependencies, navigator: NavigatorType) {
self.dependencies = dependencies
self.navigator = navigator
rootViewController.delegate = self
}
func start() {}
}
extension RegisterCoordinator: PhoneInputViewControllerDelegate {
func phoneInputDone() {
let vc = PhoneValidationViewController.instantiate(storyboardName: Constants.Storyboards.authFlow)
vc.delegate = self
navigator.push(vc, animated: true)
}
}
extension RegisterCoordinator: PhoneValidationViewControllerDelegate {
func phoneValidationDone() {
let vc = GenderSelectionViewController.instantiate(storyboardName: Constants.Storyboards.authFlow)
vc.viewModel = GenderSelectionViewModel(dependencies: dependencies)
navigator.push(vc, animated: true)
}
}
When the entire sign up flow is complete, the last page can save the user, which triggers the authStateChanged method in the AppCoordinator, which then changes the navigator's rootViewController. This should then clean up its child coordinators as well.
Sadly though, the RegisterCoordinator and its rootViewController (PhoneInputViewController) are kept alive - although the other viewcontrollers in the flow are properly released.
I tried to manually do childCoordinators = [] in the start method to make sure the AppCoordinator doesn't have a strong reference to the RegisterCoordinator, but even that doesn't help.
I have no clue what is keeping the strong reference, causing the retain cycle / memory leak. I have a super minimal version of my app with basically everything removed except the bare essentials to show the problem, up on GitHub: https://github.com/kevinrenskers/coordinator-problem.
First of all, you're capturing your coordinator inside a block in Coordinator.self line 132:
I found this using Debug Memory Graph:
there's also PhoneInputViewController still alive, you can examine why using the same method
I can't fully understand how your coordinator pattern implementation work, but it's a good idea not to keep strong refs to your controllers.
I've been using some implementation, where controllers being kept only by UINavigationController's stack, and window keeps UINavigationController.
It guarantees that your controllers will always die once popped/replaced.
In your case, I would start by trying making childCoordinators of Coordinator to keep weak refs to your controllers.
The answer from rkyr pushed me in the right direction, and I found the source of the problem and sent a PR with the fix to the original Coordinator library that I am using. So check it out there for the one-line fix: https://github.com/daveneff/Coordinator/pull/1.
I have defined a class called Person. This is my code:
class Person {
var closure: (() -> ())?
var name: String
init(name: String) {
self.name = name
print("\(name) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}
then I use Person in class called ViewController:
class ViewController: UIViewController {
var person = Person(name: "john")
let aStr = "john is a cute boy"
override func viewDidLoad() {
super.viewDidLoad()
person.closure = {
print("\(self.aStr)")
}
person.closure!()
}
}
In my opinion, the picture of memory about my code like this :
So, from above picture, in my opinion, it will cause strong reference cycle between the three instances, but I can not get any leak from Instruments, so I have some confusion.
Does this code cause strong reference cycle?
If not, when will ARC deallocate the instance of Person? the method named deinit in Person class is never called.
Yes, this's a typical retain cycle.
To solve this problem use [weak self] in your closure
person.closure = { [weak self] in
guard let strongSelf = self else { return }
print("\(strongSelf.aStr)")
}
To really create a leak.
I create a demo App. Root is a navController.
The navController has a root controller. Let's call it buttonController.
When you click button in the buttonController, it create your ViewController and push to navController.
When you click back button in navigation bar, the navController pop your ViewController instance.
Profile it, then you will see the leak and the retain cycle in Instruments.
Xcode default template of iOS App use a single page, which always retain your ViewController instance. If the ViewController instance is still used by the system, it's actually not a leak yet.
So push & pop show that leak for you.
I know that there are multiple approaches to pass data back from one controller to another like Delegates, NSNotifications. I am using another way using Closures to pass data data back. I just want to know is it safe way how I pass any data using blocks like below or should I avoid this approach.
First ViewController (where I make object of Second ViewController)
#IBAction func push(sender: UIButton) {
let v2Obj = storyboard?.instantiateViewControllerWithIdentifier("v2ViewController") as! v2ViewController
v2Obj.completionBlock = {(dataReturned) -> ()in
//Data is returned **Do anything with it **
print(dataReturned)
}
navigationController?.pushViewController(v2Obj, animated: true)
}
Second ViewController (where data is passed back to First VC)
import UIKit
typealias v2CB = (infoToReturn :NSString) ->()
class v2ViewController: UIViewController {
var completionBlock:v2CB?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func returnFirstValue(sender: UIButton) {
guard let cb = completionBlock else {return}
cb(infoToReturn: returnFirstValue)
}
#IBAction func returnSecondValue(sender: UIButton) {
guard let cb = completionBlock else {return}
cb(infoToReturn: returnSecondValue)
}
}
That's a very good and reasonable approach and much better than notifications.
Looking at the evolution of Cocoa API you will notice that Apple has replaced more and more delegate API with blocks / closures over the years.