前言
最近在开发过程中,遇到了一个因为UIViewController继承时父子类方法重名造成的一个crash问题,本文是问题的原因分析和解决方法。
内容
1.一个crash问题的分析
有如下父子类,SubViewController
继承于BaseViewController
,父子类中均有-addConstraintsForSubviews
这个重名的私有方法。
1 | //父类 |
实际运行过程中,遇到了如下的crash:
1 | Terminating app due to uncaught exception |
上述的-addConstraintsForSubviews
方法中有用到AutoLayout做自动布局,crash也是因为自动布局导致的。
经过断点调试,也就是走了一遍ViewController的生命周期方法,分析出了这个crash的原因:
- 1.子类执行
-viewDidLoad
时,调用了[super viewDidLoad]
,触发父类的-viewDidLoad
。 - 2.父类的
-viewDidLoad
方法中实现了基础的UI加载和布局,也就是父类对应的-base_lazyLoadSubviews
和-addConstraintsForSubviews
这两个方法。 - 3.当父类执行这两个方法时,其实就已经有问题了,因为子类有同名的
-addConstraintsForSubviews
方法,导致父类的方法实现被子类覆盖了,此时父类会去调用子类的-addConstraintsForSubviews
去布局。 - 4.子类此时还没有执行
-sub_lazyLoadSubviews
方法,也就是子类的视图控件还没有添加到父视图上,此时执行AutoLayout,就造成了crash。
2.解决方法
在父类增加一些共有的UI组件,某些情况下可以简化开发,但其实更多的时候,并没有省却很多麻烦。个人建议还是不要在公有的父类中增加过多的UI特性,以免日后更多的不必要的麻烦,子类过多,已经足够引起你的重视了。当然如果只是项目中的基类,用作埋点或其他用途,那么增加其他的UI特性,更是不合适的了。
下面只是基于上述问题,提出对应的解决方案。
2.1.规范命名
这其实是算是一个命名不规范导致的问题,如果是父类私有的方法,还是增加前缀比较安全,所以最简单的解决方式就是改名:-base_addConstraintsForSubviews
-sub_addConstraintsForSubviews
2.2.更加优雅的解决方式
上一种方法其实并不是很好,因为可能遇到其他开发者继承于你的父类的问题,如果每次都要告知对方去注意这些问题,就很容易出问题了。
这里提出一种参考系统的生命周期方法中回调父类的方式去解决这个问题。
在父类中的头文件,声明子类容易覆写的同类型方法:
1 | @interface BaseViewController : UIViewController |
方法声明过程中,使用了系统的宏NS_REQUIRES_SUPER
来修饰,表示子类覆写该方法时,必须在方法内部调用super的这个方法,否则会有如下的警告(以系统的UITableViewCell
的-prepareForReuse
方法为例):
这样即便子类使用者在不知情的情况下,覆写了父类的同名方法,也会有警告提示,只要执行了父类的同名方法,就可以避免上述的问题发生。
参考
本文作者:霖溦
本文链接:https://kukumalucn.github.io/blog/2018/07/31/UIViewController继承时父子类方法重名造成的一个crash/
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明出处!