IOS设计模式第七篇之观察者设计模式
版权声明:原创作品,谢绝转载!否则将追究法律责任。
观察者设计模式
在观察者设计模式里面,一个对象通知其他的对象一些状态的改变。涉及这些对象不需要知道另一个对象---因此鼓励解耦设计模式。这个设计模式经常被用来通知感兴趣的对象当一个属性被改变时候。
通常实现需要一个观察者注册另一个对象感兴趣的状态。当状态改变,所有的观察者对象被通知改变了。苹果的远程通知服务就是一个全球性的例子。
如果你一直坚持MVC的概念,你需要允许你的model对象和View对象通信,但是不能直接引用,这就是观察者设计模式的由来。
cocoa实现观察者有两个相似的方法:通知和键值观察:
通知:不要被本地通知和远程通知迷惑,通知是根据订阅和通知的模式允许一个对象(通知者)发送消息给另一些对象(订阅者也就是监听者)。这个通知者不需要知道订阅者的任何信息。
苹果公司大量的使用通知,例如当键盘隐藏时候系统发送一个UIKeyboardWillShowNotification/UIKeyboardWillHideNotification通知。当你的应用进入后台系统发送一个UIApplicationDidEnterBackgroundNotification 通知。
打开UIApplication头文件。在文件最后你会看到系统发出的20个通知。
怎么使用通知呢
在AlbumView实现文件里面插入下面代码在[self addSubview:indicator];后面initWithFrame:albumCover:里面
[[NSNotificationCenter defaultCenter] postNotificationName:@"BLDownloadImageNotification"
object:self
userInfo:@{@"imageView":coverImage, @"coverUrl":albumCover}];
这一行通过NSNotificationCenter 的一个单例发送一个通知。这个通知包含了一个UIImageView用来填充专辑封面和一个下载图片的URL。这里面的所有信息你需要在你的下载任务里面执行。
在libraryAPI实现文件里面的init方法isOnline = NO:后面添加下面代码:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadImage:) name:@"BLDownloadImageNotification" object:nil];
这个就是观察者。每次AlbumView 类发送一个BLDownloadImageNotification 通知。这个libraryAPI为这个通知注册一个观察者。系统通知libraryAPI。并且libraryAPI执行downloadImage:作为回答。
然后在实现downloadImage:之前你必须取消你注册的这个通知当你的类销毁的时候。如果不这样做通知就会发送到一个销毁的对象。这就是应用崩溃的结果。
在libraryAPI实现文件里面添加下面代码:
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
当这个类销毁时候。他作为观察者从所有已经注册的通知中移除掉。还有一件事情要做,我们保存下载的图片。这样应用不需要下载相同的图片了。
打开
PersistencyManager有文件加入下面两个方法原型:
- (void)saveImage:(UIImage*)image filename:(NSString*)filename; - (UIImage*)getImage:(NSString*)filename;
实现文件里面:
- (void)saveImage:(UIImage*)image filename:(NSString*)filename { filename = [NSHomeDirectory() stringByAppendingFormat:@"/Documents/%@", filename]; NSData *data = UIImagePNGRepresentation(image); [data writeToFile:filename atomically:YES]; } - (UIImage*)getImage:(NSString*)filename { filename = [NSHomeDirectory() stringByAppendingFormat:@"/Documents/%@", filename]; NSData *data = [NSData dataWithContentsOfFile:filename]; return [UIImage imageWithData:data]; }
这段代码是下载图片保存在沙盒,并且如果沙盒没有这个图片路径就会返回nil。
下面在libraryAPI加入下面代码:
- (void)downloadImage:(NSNotification*)notification { // 1 UIImageView *imageView = notification.userInfo[@"imageView"]; NSString *coverUrl = notification.userInfo[@"coverUrl"]; // 2 imageView.image = [persistencyManager getImage:[coverUrl lastPathComponent]]; if (imageView.image == nil) { // 3 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ UIImage *image = [httpClient downloadImage:coverUrl]; // 4 dispatch_sync(dispatch_get_main_queue(), ^{ imageView.image = image; [persistencyManager saveImage:image filename:[coverUrl lastPathComponent]]; }); }); } }
具体解释上面代码
1:downloadImage 被执行通过通知因此接收这个通知对象作为参数:
2:如果之前已经下载图片那么从PersistencyManager 检索图片。
3:如果图片没有下载,那么再次用HTTPClient请求图片
4:当下载完成了在UIImageView里面显示图片并且用PersistencyManager 来保存图片。
我们再次用外观设计模式隐藏从一些类里面下载一个图片的复杂性。这个通知发送者不关心你的图片是从网络获取的还是从文件系统获得的。
编译运行你的应用看看你的专辑已经覆盖到你的滑动视图上。
再次停止并且运行他。注意没有延迟加载你的专辑因为他们已经保存到本地。你甚至可以断开网络链接你的应用还可以继续的完美运行着。但是你的网络提示器一直的旋转不停止了为什么呢?
当你开始下载图片,你没有实现当图片下载完停止网络提示器的旋转的逻辑。你不能每次都发送一个通知当图片下载完成了。然而我们可以用另一个观察者设计模式键值编码。