观察者模式 - Observer
在观察者模式里,一个对象在状态变化的时候会通知另一个对象。参与者并不需要知道其他对象的具体是干什么的 - 这是一种降低耦合度的设计。这个设计模式常用于在某个属性改变的时候通知关注该属性的对象。
常见的使用方法是观察者注册监听,然后再状态改变的时候,所有观察者们都会收到通知。
在 MVC 里,观察者模式意味着需要允许 Model
对象和 View
对象进行交流,而不能有直接的关联。
Cocoa
使用两种方式实现了观察者模式: Notification
和 Key-Value Observing (KVO)
。
通知 - Notification
不要把这里的通知和推送通知或者本地通知搞混了,这里的通知是基于订阅-发布模型的,即一个对象 (发布者) 向其他对象 (订阅者) 发送消息。发布者永远不需要知道订阅者的任何数据。
Apple
对于通知的使用很频繁,比如当键盘弹出或者收起的时候,系统会分别发送 UIKeyboardWillShowNotification/UIKeyboardWillHideNotification
的通知。当你的应用切到后台的时候,又会发送 UIApplicationDidEnterBackgroundNotification
的通知。
注意:打开 UIApplication.swift
文件,在文件结尾你会看到二十多种系统发送的通知。
如何使用通知
打开 AlbumView.swift
然后在 init
的最后插入如下代码:
NSNotificationCenter.defaultCenter().postNotificationName("BLDownloadImageNotification", object: self, userInfo: ["imageView":coverImage, "coverUrl" : albumCover])
这行代码通过 NSNotificationCenter
发送了一个通知,通知信息包含了 UIImageView
和图片的下载地址。这是下载图像需要的所有数据。
然后在 LibraryAPI.swift
的 init
方法的 super.init()
后面加上如下代码:
NSNotificationCenter.defaultCenter().addObserver(self, selector:"downloadImage:", name: "BLDownloadImageNotification", object: nil)
这是等号的另一边:观察者。每当 AlbumView
发出一个 BLDownloadImageNotification
通知的时候,由于 LibraryAPI
已经注册了成为观察者,所以系统会调用 downloadImage()
方法。
但是,在实现 downloadImage()
之前,我们必须先在 dealloc
里取消监听。如果没有取消监听消息,消息会发送给一个已经销毁的对象,导致程序崩溃。
在 LibaratyAPI.swift
里加上取消订阅的代码:
deinit {NSNotificationCenter.defaultCenter().removeObserver(self)
}
当对象销毁的时候,把它从所有消息的订阅列表里去除。
这里还要做一件事情:我们最好把图片存储到本地,这样可以避免一次又一次下载相同的封面。
打开 PersistencyManager.swift
添加如下代码:
func saveImage(image: UIImage, filename: String) {let path = NSHomeDirectory().stringByAppendingString("/Documents/\(filename)")let data = UIImagePNGRepresentation(image) data.writeToFile(path, atomically: true) } func getImage(filename: String) -> UIImage? { var error: NSError? let path = NSHomeDirectory().stringByAppendingString("/Documents/\(filename)") let data = NSData(contentsOfFile: path, options: .UncachedRead, error: &error) if let unwrappedError = error { return nil } else { return UIImage(data: data!) } }
代码很简单直接,下载的图片会存储在 Documents
目录下,如果没有检查到缓存文件, getImage()
方法则会返回 nil
。
然后在 LibraryAPI.swift
添加如下代码:
func downloadImage(notification: NSNotification) {//1let userInfo = notification.userInfo as [String: AnyObject]var imageView = userInfo["imageView"] as UIImageView? let coverUrl = userInfo["coverUrl"] as NSString //2 if let imageViewUnWrapped = imageView { imageViewUnWrapped.image = persistencyManager.getImage(coverUrl.lastPathComponent) if imageViewUnWrapped.image == nil { //3 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in let downloadedImage = self.httpClient.downloadImage(coverUrl) //4 dispatch_sync(dispatch_get_main_queue(), { () -> Void in imageViewUnWrapped.image = downloadedImage self.persistencyManager.saveImage(downloadedImage, filename: coverUrl.lastPathComponent) }) }) } } }
拆解一下上面的代码:
downloadImage
通过通知调用,所以这个方法的参数就是NSNotification
本身。UIImageView
和URL
都可以从其中获取到。- 如果以前下载过,从
PersistencyManager
里获取缓存。 - 如果图片没有缓存,则通过
HTTPClient
获取。 - 如果下载完成,展示图片并用
PersistencyManager
存储到本地。
再回顾一下,我们使用外观模式隐藏了下载图片的复杂程度。通知的发送者并不在乎图片是如何从网上下载到本地的。
运行一下项目,可以看到专辑封面已经显示出来了:
关了应用再重新运行,注意这次没有任何延时就显示了所有的图片,因为我们已经有了本地缓存。我们甚至可以在没有网络的情况下正常使用我们的应用。不过出了问题:这个用来提示加载网络请求的小菊花怎么一直在显示!
我们在下载图片的时候开启了这个白色小菊花,但是在图片下载完毕的时候我们并没有停掉它。我们可以在每次下载成功的时候发送一个通知,但是我们不这样做,这次我们来用用另一个观察者模式: KVO 。
键值观察 - KVO
在 KVO 里,对象可以注册监听任何属性的变化,不管它是否持有。如果感兴趣的话,可以读一读苹果 KVO 编程指南。
如何使用 KVO
正如前面所提及的, 对象可以关注任何属性的变化。在我们的例子里,我们可以用 KVO 关注 UIImageView
的 image
属性变化。
打开 AlbumView.swift
文件,找到 init(frame:albumCover:)
方法,在把 coverImage
添加到 subView
的代码后面添加如下代码:
coverImage.addObserver(self, forKeyPath: "image", options: nil, context: nil)
这行代码把 self
(也就是当前类) 添加到了 coverImage
的 image
属性的观察者里。
在销毁的时候,我们也需要取消观察。还是在 AlbumView.swift
文件里,添加如下代码:
deinit {coverImage.removeObserver(self, forKeyPath: "image")
}
最终添加如下方法:
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) { if keyPath == "image" { indicator.stopAnimating() } }
必须在所有的观察者里实现上面的代码。在检测到属性变化的时候,系统会自动调用这个方法。在上面的代码里,我们在图片加载完成的时候把那个提示加载的小菊花去掉了。
再次运行项目,你会发现一切正常了:
注意:一定要记得移除观察者,否则如果对象已经销毁了还给它发送消息会导致应用崩溃。
此时你可以把玩一下当前的应用然后再关掉它,你会发现你的应用的状态并没有存储下来。最后看见的专辑并不会再下次打开应用的时候出现。
为了解决这个问题,我们可以使用下一种模式:备忘录模式。
转载于:https://www.cnblogs.com/Free-Thinker/p/5090777.html
观察者模式 - Observer相关推荐
- 设计模式:观察者模式--Observer
一.什么是观察者模式 1.生活中的观察者模式 1.警察抓小偷 在现实生活中,警察抓小偷是一个典型的观察者模式「这以一个惯犯在街道逛街然后被抓为例子」,这里小偷就是被观察者,各个干警就是观察者,干警时时 ...
- 设计模式 ( 十六 ) 观察者模式Observer(对象行为型)
设计模式 ( 十五 ) 观察者模式Observer(对象行为型) 1.概述 一些面向对象的编程方式,提供了一种构建对象间复杂网络互连的能力.当对象们连接在一起时,它们就可以相互提供服务和信息. 通常来 ...
- 观察者模式 Observer 发布订阅模式 源 监听 行为型 设计模式(二十三)
观察者模式 Observer 意图 定义对象一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖他的对象都得到通知并自动更新. 别名:依赖(Dependents),发布订阅(Publish-Su ...
- java设计模式--观察者模式(Observer)
java设计模式--观察者模式(Observer) java设计模式--观察者模式(Observer) 观察者模式的定义: 定义对象间的一种一对多的依赖关系.当一个对象的状态发生改变时,所有依赖于它的 ...
- 观察者模式(Observer) 简介
一, 观察者模式(Observer) 的定义 观察者模式: 定义了一种 1对多 的依赖关系, 让多个观察者对象同时监听1个主题对象. 这个主题对象在状态发生变化时, 会通知所有的观察者对象, 使它 ...
- c++观察者模式observer
c++观察者模式observer 概念 角色和职责 典型应用 案例 概念 Observer模式是行为模式之一,它的作用是当一个对象的状态发生变化时,能够自动通知其他关联对象,自动刷新对象状态. Obs ...
- Javascript乱弹设计模式系列(1) - 观察者模式(Observer)
前言 博客园谈设计模式的文章很多,我也受益匪浅,包括TerryLee.吕震宇等等的.NET设计模式系列文章,强烈推荐.对于我,擅长于前台代码的开发,对于设计模式也有一定的了解,于是我想结合Javasc ...
- 设计模式 - 观察者模式(Observer Pattern) Java内置 用法
观察者模式(Observer Pattern) Java内置 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26601659 ...
- 设计模式初探-观察者模式(OBSERVER)又称发布-订阅(Publish-Subscribe)依赖(Dependents)
观察者模式(OBSERVER),又称发布-订阅(Publish-Subscribe),依赖(Dependents),通过定义对象间的一对多的依赖关系,达到当一个对象的状态发生改变时,所有依赖于它的对象 ...
- Android开发中常见的设计模式深入浅出——观察者模式Observer
##最近老大写的Android项目里用到了RxBus然后我就去百度了 让我先了解RxJava 然后RxJava又是由观察者模式的变种写的 所以打算从头学一遍!!! 观察者模式 Observer 顾名思 ...
最新文章
- 【读书笔记】iOS-网络-解析响应负载
- MVC已经死了,接下来会发生什么?
- Halcon算子翻译——dev_set_line_width
- javaweb学习总结三(枚举)
- 纯c语言贪吃蛇,纯C语言贪吃蛇 求助
- 转: HTTP 错误 401.1 - 未经授权:访问由于凭据无效被拒绝的另类解决方案
- go int32不能打印0_Go并发实战--sync WaitGroup
- linux fastQC 操作命令,[Bio-Info]fq文件解析统计工具:FastQC在linux下初应用
- Gartner发布2020年十大战略科技发展趋势
- Android 系统(115)---死机问题分析
- 190602每日一句
- J2ME开发入门(老java游戏)
- mac安装maven
- JAVA多用户商城系统源码
- HMC5883L电子罗盘原理及应用,全网最详细~
- 硬盘测试软件得分数据怎么看,SSD硬盘测试结果分析怎么看的
- H - Streets of Working Lanterns Gym - 101149H -括号匹配-栈模拟
- Android视频直播的实现(推流完整实现001)
- laya 和 egret 区别
- 单击按钮弹出一个页面,并使背景颜色变为灰色
热门文章
- edge chrome Android,微软Edge浏览器安卓版已上架:采用Chrome引擎
- android 自动生成aidl,[Android]用AIDL生成Service
- linux vi编辑撤销,vi撤销命令(u和U),撤销上一次的操作
- cent mysql 配置,centos下MySQL安装配置
- 笔记︱集成学习Ensemble Learning与树模型、Bagging 和 Boosting、模型融合
- 基于Java的智能问答系统
- Three.js 学习笔记 - 给跳一跳小游戏添加光源,阴影
- oracle kill所有plsql developer进程
- UIButton设置圆角和边框
- 关于nova-manage service list检测服务状态原理