-
Notifications
You must be signed in to change notification settings - Fork 138
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Memory leak from HubConnection and HubConnectionConnectionDelegate #297
base: master
Are you sure you want to change the base?
Conversation
…increases the reference count over the parent class
Before you dig the rabbit hole deeper, can you:
As it stands now, the issue mentions a leak between HubConnection and HubConnectionConnectionDelegate, but the change is trying to change many other references to weak references, seeing what sticks. Plus, a lot of included changes seem unrelated. |
Thank you Pawel for your fast replay.
You can replace the code of viewDidAppear with this one ( the only difference is that this one starts 10 connections but with out keeping them in any variable only the last one in chatHubConnection ) override func viewDidAppear(_ animated: Bool) {
let alert = UIAlertController(title: "Enter your Name", message:"", preferredStyle: UIAlertController.Style.alert)
alert.addTextField() { textField in textField.placeholder = "Name"}
let OKAction = UIAlertAction(title: "OK", style: .default) { action in
self.name = alert.textFields?.first?.text ?? "John Doe"
(1...10).forEach { item in
let randomTimeAwait = Double.random(in: 0.5..<0.7) * Double(Int.random(in: 1..<7))
DispatchQueue.global().asyncAfter(deadline: .now() + randomTimeAwait) { [weak self] in
guard let self else { return }
self.chatHubConnectionDelegate = ChatHubConnectionDelegate(controller: self)
let connection = HubConnectionBuilder(url: URL(string: self.serverUrl)!)
.withLogging(minLogLevel: .debug)
.withAutoReconnect()
.withHubConnectionDelegate(delegate: self.chatHubConnectionDelegate!)
.build()
self.chatHubConnection = connection
self.chatHubConnection!.on(method: "NewMessage", callback: {[weak self] (user: String, message: String) in
guard let self else { return }
self.appendMessage(message: "\(user): \(message)")
})
self.chatHubConnection!.start()
DispatchQueue.global().asyncAfter(deadline: .now() + 3) {
connection.stop()
}
}
}
}
alert.addAction(OKAction)
self.present(alert, animated: true)
} At this example in XCode memory graph you can find out many instances without any reference in viewcontroller param chatHubConnection aka zombie objects As you can see many of them ( leaks ) are in dispatch_queue or swift closure context due to "self" is capturing inside the callback. P.S. If you agree i can move in a deeper investigation on closures and delegate, this PR is a fast finding fix not a deep search on earch row of your code. |
Thanks a lot for the details! I will take a deeper look soon. |
Thanks !! Feel free to contact me for what ever you need ( i find out another two i think were last but i will push them after the first review ) |
I was trying your repro today and didn't seem to be able to get the same result. This is not to say that I don't believe there are leaks in the code. I received PRs fixing leaks in the past, but they were only piecemeal. I will need to run a more systematic analysis to review code that can leak. It feels like a time-consuming task, so it will take a while. |
now i see it, expand the SignalRClient (127 items) !!! this is the lib, your snapshot shows the VC ( app ) part DispatchQueue.global().asyncAfter(deadline: .now() + 3) {
connection.stop()
} |
Due to Swift is a retain counting memory management language,
recommended the usage of "weak" reference at delegate due to strong reference
will leak on a retain cycle and a zobby object.
In this case we have a memory leak between HubConnection and HubConnectionConnectionDelegate, the way to reproduce it is easy.
In ViewController add a foreach ( 5 times ) method in OKAction ( row 37-48 ), start SignalR server and run the application,
after a couple of seconds you will notice in memory grapgh that there are many instance of HubConnection where should exist only one due to variable chatHubConnection. This "zobby" HubConnection exists due to retain cycle between HubConnection and HubConnectionConnectionDelegate.