开发墨迹天气App中遇到的问题总结(一)

1UIViewController生命周期函数勿忘调用相应的super函数

背景

当我们创建一个UIViewController类的对象时,通常系统会生成几个默认的方法,这些方法大多与视图的调用有关。

通常上述方法包括如下几种,这些方法都是UIViewController类的方法:

1
2
3
4
5
6
- (void)viewDidLoad;
- (void)viewDidUnload;
- (void)viewWillAppear:(BOOL)animated;
- (void)viewDidAppear:(BOOL)animated;
- (void)viewWillDisappear:(BOOL)animated;
- (void)viewDidDisappear:(BOOL)animated;

详见UIViewController的生命周期及iOS程序执行顺序

iOS工程有一种常见的工程实践方法——项目中做一个base UIViewController,假设为BaseViewController。之后,所有页面控制器不要直接继承UIViewController,而是继承BaseViewController。BaseViewController针对所有的页面控制器做一些通用设置。比如:设置navigation bar的颜色,设置navigation bar的默认按钮,通用事件统计(如,某个页面控制器的展示次数),通用全局手势,通用内存警告处理等。

现象

某次调试发现自定义的页面控制器并未正确设置navigation bar的颜色。代码如下:

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
34
@interface BaseViewController : UIViewController
@end

@implementation BaseViewController

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];

//其他代码...

//设置导航条外观
[self setNavigationBarAppearance];

//其他代码...
}

- (void) hellWorld {
[super helloWo];
}

@end

@interface MyViewController : BaseViewController
@end

@implementation MyViewController

- (void)viewWillAppear:(BOOL)animated {
//[super viewWillAppear:animated];

//其他代码...
}

@end

解释

BaseViewController在- (void)viewWillAppear:(BOOL)animated函数做了导航条外观的通用设置。而,MyViewController并未在- (void)viewWillAppear:(BOOL)animated的时候调用其父类的相应函数,因此navigation bar的外观设置会出错。

解法

如果你自己实现了UIViewController生命周期相关函数,那么需要在该函数最开始调用其父类的相应函数。

2NSNotification问题

背景

NSNotification是iOS上多种事件通知机制的一种。常用于一对多的通知。

BaseViewController在处理非当前展示控制器收到的内存警告的时候总会把self.view设置为nil。代码如下:

1
2
3
4
5
6
7
8
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
if ([[UIDevice currentDevice].systemVersion floatValue] >= 6.0) {
if (self.isViewLoaded && !self.view.window) {
self.view = nil;
}
}
}

现象

某次发现,MyViewController收到@”FooNotification”通知时会多次调用- (void)p_onFooNotification:(NSNotification *)notify函数。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@interface MyViewController : BaseViewController
@end

@implementation MyViewController

- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(p_onFooNotification:)
name:@"FooNotification"
object:nil];
//其他代码...
}

- (void)p_onFooNotification:(NSNotification *)notify {
//其他代码
}

@end

解释

某次收到了内存警告,MyViewController正好不在界面上,(是底部controller)。BaseViewController的- (void)didReceiveMemoryWarning会将self.view设置为nil。下次MyViewController显示的时候会再调用一次- (void)viewDidLoad。这时又添加了一次@”FooNotification”通知的观察。所以通知一旦发出,- (void)p_onFooNotification:(NSNotification *)notify函数会被调用多次。

解法

1)了解- (void)viewDidLoad函数可能会被调用多次,只要self.view 是nil。在controller显示出来的时候就会调用该函数;

2)在controller中添加对通知的观察最好不要放在- (void)viewDidLoad函数中;推荐做法是在- (void)viewDidAppear:(BOOL)animated中添加对通知的观察,- (void)viewDidDisappear:(BOOL)animated中移除对通知的观察。例如NSNotification errors所作的解答。