首页 » iOS编程(第4版) » iOS编程(第4版)全文在线阅读

《iOS编程(第4版)》18.6 NSNotificationCenter和内存过低警告

关灯直达底部

当iOS设备内存不足时,系统会向运行中的应用发送一条内存过低警告通知。应用收到该通知后,应该立刻释放当前不需要使用的资源以及后期可以重新创建的对象。视图控制器在收到该通知的同时还会收到didReceiveMemoryWarning消息。

除了视图控制器,其他对象中也可能存在需要及时释放的数据。BNRImageStore对象就是如此:它可以释放当前没有显示的UIImage对象,等到需要时再从文件系统载入。

为了使视图控制器以外的对象也可以在内存不足时释放数据,必须使用通知中心(notification center)。每一个iOS应用中都有一个NSNotificationCenter对象,读者可以将其视作智能的通知栏,对象可以将自己注册为某个通知的观察者(observer),例如:“如果有人找到了我丢失的狗,请立刻通知我。”当另一个对象发送了一条通知:“我找到了一条狗。”通知中心就会将这条通知发送给对应的观察者。

内存过低警告通知的名称是UIApplicationDidReceiveMemoryWarning- Notification。凡是需要自己处理内存过低警告的对象,都可以注册并接收这个通知。编辑BNRImageStore.m中的initPrivate方法,将BNRImageStore对象注册为UIApplicationDidReceiveMemoryWarningNotification通知的观察者,代码如下:

- (instancetype)initPrivate

{

self = [super init];

if (self) {

_dictionary = [[NSMutableDictionary alloc] init];

NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

[nc addObserver:self

selector:@selector(clearCache:)

name:UIApplicationDidReceiveMemoryWarningNotification

object:nil];

}

return self;

}

现在BNRImageStore对象已经注册为通知中心的观察者了(见图18-8)。

图18-8 注册为通知中心的观察者

当系统发出内存过低警告通知时,BNRImageStore对象会收到UIApplicationDid- ReceiveMemoryWarningNotification通知,并调用clearCache:方法清除缓存(见图18-9)。

图18-9 接收通知

在BNRImageStore.m中实现clearCache:,移除BNRImageStore对象所包含的所有UIImage对象。

- (void)clearCache:(NSNotification *)note

{

NSLog(@“flushing %d images out of the cache”, [self.dictionary count]);

[self.dictionary removeAllObjects];

}

当字典对象移除某个对象时,被移除对象的拥有方个数会减1。因此,当BNRImageStore对象移除其包含的所有UIImage对象时,这些UIImage对象的拥有方个数都会减1。减1后,没有拥有方的UIImage对象会被释放,并在需要时由BNRImageStore对象根据相应的文件再次创建。有些UIImage对象可能还会有其他拥有方,这些UIImage对象不会被释放。例如,BNRDetailViewController对象的imageView会保留某个UIImage对象,那么该对象不会被释放。当imageView放弃之前保留的UIImage对象时(例如UINavigationController对象弹出了BNRDetailViewController对象,或者应用将新的UIImage对象赋给了imageView),相应的UIImage对象才会被释放。

在模拟器中构建并运行应用,加入一些图片,然后在Hardware(硬件)菜单中选择Simulate Memory Warning(模拟内存警告),控制台中会输出日志信息,提示缓存图片已经被清除。

深入学习NSNotificationCenter

通知与委托、目标-动作相同,也是回调的一种形式。它们的区别是,委托和目标-动作需要对象直接向自己的委托或目标发送消息,而通知使用一位中介者:NSNotificationCenter。

Objective-C使用NSNotification表示通知,每个NSNotification对象都具有名称(NSNotificationCenter根据该名称检索此通知的所有观察者)、来源对象(发布该通知的对象)和可选的userInfo字典(来源对象需要告诉观察者的额外信息)。例如,当系统状态栏的frame发生变化后,UIApplication会发布名为UIApplicationDidChangeStatus- BarFrameNotification的通知,并带有一个存储状态栏的新frame的userInfo字典。如果代码中收到了该通知,则可以使用以下方式获取状态栏的新frame:

- (void)statusBarMovedOrResized:(NSNotification *)note

{

NSDictionary *userInfo = [note userInfo];

NSValue *wrappedRect = userInfo[UIApplicationStatusBarFrameUserInfoKey];

CGRect newFrame = [wrappedRect CGRectValue];

……使用newFrame……

}

需要注意的是,如果需要向NSDictionary中存储CGRect结构,必须先将其包装为NSValue对象——NSDictionary中只能存储Objective-C对象。

那么,如何了解userInfo字典中存储了哪些内容呢?类参考手册中会详细说明类发布的通知,大部分通知的说明都是:“This notification does not contain a userInfo dictionary.(该通知不会附带userInfo字典。)”对于带有userInfo字典的通知,文档中会列出字典中的所有键及其对应值的含义。

addObserver:selector:name:object:方法的最后一个参数通常是nil——假设观察的通知名称为“Fire!”,传入nil表示接受所有对象发布的“Fire!”通知。如果为该参数传入某个对象,那么观察者只会接受该对象发布的“Fire!”通知,其他对象发布的“Fire!”通知则会被忽略。

通知允许多个对象将回调函数注册到相同的事件上。同一个通知可以有许多观察者,当通知发布后,所有观察者都会执行之前注册的回调函数(同时执行,没有先后顺序)。如果多个对象需要监听同一个事件,则通知是最好的解决方案。例如,许多对象都需要在设备方向发生变化时执行一系列操作,因此该事件发生时系统会发布名为UIDeviceOrientationDid- ChangeNotification的通知。

最后需要说明的是,NSNotification和NSNotificationCenter与用户看到的推送通知(push notification)或本地通知(local notification)无关,NSNotificationCenter只用来处理应用内部的对象通信。