I’m quite new with iOS development and while testing for leaks found out that UITabBaritem
isn’t deallocating and have 1:1 reference with UITabBarItemBridgedElement
I’m using Xcode 16 with support from iOS 15.
so i was wondering if this is because of my implementation or there is some Apple bug with it?
MVP
class MainViewController: UIViewController {
var button: UIButton = {
var config = UIButton.Configuration.filled()
config.cornerStyle = .capsule
config.title = "Tap me"
config.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 20, bottom: 5, trailing: 20)
config.baseBackgroundColor = .tintColor
let button = UIButton(configuration: config)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
title = "Main View Controller"
view.backgroundColor = .white
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
view.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: self.view.centerYAnchor)
])
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("MAIN WILL APPEAR")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
print("VC \(self)\(title) recieved mem warning")
}
override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent)
print("VC \(self)\(title) will move to parent")
}
@objc
func buttonAction() {
let redVC: UINavigationController = createRedNVC()
let greenVC: UINavigationController = createGreenNVC()
let blueVC: UINavigationController = createBlueNVC()
let tabBarVC = TabBarController()
tabBarVC.setViewControllers([redVC, greenVC, blueVC], animated: false)
navigationController?.pushViewController(tabBarVC, animated: true)
}
private func createRedNVC() -> UINavigationController {
let root = ViewControllerRed()
let nav = UINavigationController(rootViewController: root)
nav.tabBarItem = UITabBarItem(title: "Red", image: nil, tag: 0)
return nav
}
private func createGreenNVC() -> UINavigationController {
let root = ViewControllerGreen()
let nav = UINavigationController(rootViewController: root)
nav.tabBarItem = UITabBarItem(title: "Green", image: nil, tag: 0)
return nav
}
private func createBlueNVC() -> UINavigationController {
let root = ViewControllerBlue()
let nav = UINavigationController(rootViewController: root)
nav.tabBarItem = UITabBarItem(title: "Blue", image: nil, tag: 0)
return nav
}
}
class ViewControllerBlue: UIViewController {
deinit {
print("OS reclaiming memory for ViewControllerBlue")
// tabBarItem = nil
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .blue
title = "Blue"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
print("VC \(self) recieved mem warning")
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
print("VC \(self) will disappear")
}
override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent)
print("VC \(self) will move to parent:\(parent)")
}
}
class ViewControllerRed: UIViewController {
deinit {
print("OS reclaiming memory for ViewControllerRed")
// tabBarItem = nil
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
title = "Red"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
print("VC \(self)\(title) recieved mem warning")
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
print("VC \(self)\(title) will disappear")
}
override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent)
print("VC \(self)\(title) will move to parent:\(parent)")
}
}
class ViewControllerGreen: UIViewController {
deinit {
print("OS reclaiming memory for ViewControllerGreen")
// tabBarItem = nil
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
title = "Green"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
print("VC \(self)\(title) recieved mem warning")
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
print("VC \(self)\(title) will disappear")
}
override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent)
print("VC \(self)\(title) will move to parent:\(parent)")
}
}
class TabBarController: UITabBarController {
deinit {
print("OS reclaiming memory for TabBarController")
}
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
self.tabBar.backgroundColor = .systemGray
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
guard let navigationController else { return }
print("TabBarController will disappear, navController: \(navigationController)")
}
override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent)
print("TabBarController will move to parrent: \(parent) \(self.viewControllers)")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
print("TabBarController recieved mem warning")
}
}
extension TabBarController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
print("Selected: \(viewController)")
}
}
And from Instruments>Leaks i get this
Only thing that seems to work is to set tabBarItem = nil
in deinit in every VC.
I’ve tried every solution that i could find here, without success
If someone have experience with this would be much appreciated, pulling my hair for few days now about this problem