写个简单的Swift检测Controller没有销毁的工具

img

思路

在Swift的代码中,Controller没有销毁大部分的原因都是没有weak self。怎么检测一个Controller没有释放呢?

1、当一个对象销毁的时候,它会调用deinit的方法,一般Controller页面我们都是放在UINavigationController里面的,然后调用push和pop,实现我们页面的跳转。

2、继承UINavigationController,重写push方法,在push方法里面把push的controller的名字放到单例的数组里面,deinit的时候在把当前的controller从单例里面释放,然后检测单例里面有没有controller的相同名字存在2个以上的。

代码实现

因为是检测Controller有没有销毁的工具,然后打印到console里查看,所以代码应该在DEBUG模式下执行。

如果使用的UI架构师tabbar加几个controllers的样式,初始化tabbar的时候会调用push。所以判断如果是tabbarcontroller直接return

1
2
3
4
5
func defaultController() -> UINavigationController {
let navi = JYNavigationViewController.init(rootViewController: JYTabBarViewController())
return navi
}

1
2
3
4
5
6
7
8
9
10
11
func pushVc(_ vc: UIViewController) {
#if DEBUG
if vc is JYTabBarViewController {
return
}
vcs.append(NSStringFromClass(vc.classForCoder))
#endif
}

在继承的NavigationController实现push代码

1
2
3
4
5
6
7
8
9
10
11
override open func pushViewController(_ viewController: UIViewController, animated: Bool) {
viewController.hidesBottomBarWhenPushed = self.viewControllers.count > 0
if self.viewControllers.count > 0 {
viewController.showLeftButton()
}
CheckWselfHelper.shared.pushVc(viewController)
super.pushViewController(viewController, animated: animated)
}

在pop代码里调用数组的filter函数,过滤掉当前controller名字相同的内容,然后遍历数组,筛选出数组中名字相同有大于1个controller,并打印出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func popVc(_ vc: UIViewController?) {
#if DEBUG
guard let vc = vc else {return}
let str = NSStringFromClass(vc.classForCoder)
vcs = vcs.filter({$0 != str})
var datas = [String: Int]()
vcs.forEach({ (str) in
datas[str] = (datas[str] ?? 0)+1
})
datas.forEach({ (key, value) in
if value > 1 {
Log.i("注意"+key+"没有释放")
}
})
#endif
}

在BaseController的deinit方法里实现我们的pop函数

1
2
3
deinit {
CheckWselfHelper.shared.popVc(self)
}

由于我是tabba的ui架构,所以在点击tabbar的时候会push很多tabbar和navigation的controller。所以在点击和初始化的时候清空下我们的单例数组。

1
2
3
4
5
6
7
8
9
10
11
func clearVcs(_ addVc: UIViewController?) {
#if DEBUG
vcs.removeAll()
guard let addVc = addVc else {return}
pushVc(addVc)
#endif
}

在tabbar里面的实现如下

初始化的时候清空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
func initViews() {
let tvc = JYTicketViewController()
let ticketVc: JYNavigationViewController = JYNavigationViewController(rootViewController: tvc)
let mallVc: JYNavigationViewController = JYNavigationViewController(rootViewController: JYMallViewController())
let mineVc: JYNavigationViewController = JYNavigationViewController(rootViewController: JYMineViewController())
ticketVc.tabBarItem.title = "购票"
ticketVc.tabBarItem.image = UIImage.init(named: "ticket_normal")?.withRenderingMode(.alwaysOriginal)
ticketVc.tabBarItem.selectedImage = UIImage.init(named: "ticket_selected")?.withRenderingMode(.alwaysOriginal)
mallVc.tabBarItem.title = "商城"
mallVc.tabBarItem.image = UIImage.init(named: "mall_normal")?.withRenderingMode(.alwaysOriginal)
mallVc.tabBarItem.selectedImage = UIImage.init(named: "mall_selected")?.withRenderingMode(.alwaysOriginal)
mineVc.tabBarItem.title = "我的"
mineVc.tabBarItem.image = UIImage.init(named: "mine_normal")?.withRenderingMode(.alwaysOriginal)
mineVc.tabBarItem.selectedImage = UIImage.init(named: "mine_selected")?.withRenderingMode(.alwaysOriginal)
self.tabBar.barTintColor = UIColor.white
self.tabBar.tintColor = AppConfig.mainColor
let lists = [ticketVc, mallVc, mineVc]
self.viewControllers = lists
self.delegate = self
self.selectedIndex = 0
ticketVc.navigationBar.isTranslucent = false
mallVc.navigationBar.isTranslucent = false
mineVc.navigationBar.isTranslucent = false
CheckWselfHelper.shared.clearVcs(tvc)//这里初始化默认选中的controller
}

点击tabbar的时候清空

1
2
3
4
5
6
// MARK: UITabBarControllerDelegate
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
CheckWselfHelper.shared.clearVcs(viewController.childViewControllers.first)
return true
}

CheckWselfHelper.swift

https://gist.github.com/jackyshan/7a084291a03ae55815631697be1ae995