iOS中的observer(观察者设计模式)swift版

转载 2015年11月18日 16:49:45

观察者模式 - Observer

在观察者模式里,一个对象在状态变化的时候会通知另一个对象。参与者并不需要知道其他对象的具体是干什么的 - 这是一种降低耦合度的设计。这个设计模式常用于在某个属性改变的时候通知关注该属性的对象。

常见的使用方法是观察者注册监听,然后再状态改变的时候,所有观察者们都会收到通知。

在 MVC 里,观察者模式意味着需要允许 Model 对象和 View 对象进行交流,而不能有直接的关联。

Cocoa 使用两种方式实现了观察者模式: NotificationKey-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.swiftinit 方法的 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) {
    //1
    let 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 本身。 UIImageViewURL 都可以从其中获取到。
  • 如果以前下载过,从 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 (也就是当前类) 添加到了 coverImageimage 属性的观察者里。

在销毁的时候,我们也需要取消观察。还是在 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()
    }
}

必须在所有的观察者里实现上面的代码。在检测到属性变化的时候,系统会自动调用这个方法。在上面的代码里,我们在图片加载完成的时候把那个提示加载的小菊花去掉了。

再次运行项目,你会发现一切正常了:

注意:一定要记得移除观察者,否则如果对象已经销毁了还给它发送消息会导致应用崩溃。

此时你可以把玩一下当前的应用然后再关掉它,你会发现你的应用的状态并没有存储下来。最后看见的专辑并不会再下次打开应用的时候出现。

为了解决这个问题,我们可以使用下一种模式:备忘录模式。

设计模式——观察者模式(Observer)

要想正确理解设计模式,首先必须明确它是为了解决什么问题而提出来的。 设计模式学习笔记,欢迎交流。 ——Shulin 转载请注明出处:http://blog.csdn.net/zhsh...
  • u012909091
  • u012909091
  • 2014年08月20日 16:52
  • 2942

Java面向对象设计模式(十五)——观察者模式(Observer)

相关文档:Java面向对象设计模式 观察者模式(Observer)         包括这个模式在内的接下来的四个模式,都是类和类之间的关系,不涉及到继承,学的时候应该 记得归纳,记得本文...
  • u011814346
  • u011814346
  • 2017年05月08日 14:54
  • 208

浅谈JAVA设计模式之——观察者模式(Observer)

一、概述 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 二、适用性 1.当一个抽象模型有两个方面,其中一个方面依赖于另一方面。 将这二者封装...
  • l1028386804
  • l1028386804
  • 2015年05月09日 13:22
  • 1110

Java设计模式——观察者模式(Observer Pattern)

场景一 描述:《孙子兵法》有云:“知彼知己,百战不殆;不知彼而知己,一胜一负;不 知彼,不知己,每战必殆”,那怎么才能知己知彼呢?知己是很容易的,自己的军队嘛,很容易知道,那怎么知彼呢?安插间谍是很...
  • u012401711
  • u012401711
  • 2016年09月08日 21:33
  • 621

设计模式 - 观察者模式(Observer Pattern) 详解

观察者模式(Observer Pattern) 详解 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权...
  • u012515223
  • u012515223
  • 2014年05月22日 14:37
  • 2972

设计模式(行为型)之观察者模式(Observer Pattern)

观察者模式用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应。在观察者模式中,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应...
  • yanbober
  • yanbober
  • 2015年05月04日 20:46
  • 5103

设计模式-深入解析观察者模式(Observer)

在上篇设计模式上我们继续前进把,苦逼(酷毙)的程序员一刻都不能松懈的,要紧跟脚步,每天都学习啊!!学了策略模式,我自己去用设计模式写了几个例子,感觉还扣以啊。下面继续总结一下观察者模式的学习,如有不正...
  • qq_29282475
  • qq_29282475
  • 2015年12月07日 16:16
  • 1312

PHP设计模式之观察者模式(Observer)详细介绍和代码实例

【意图】 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新【GOF95】 又称为发布-订阅(Publish-Subscribe)模式、模型-视图...
  • longjef
  • longjef
  • 2016年11月02日 09:51
  • 3707

Android设计模式之观察者模式(Observer Pattern)

观察者模式介绍观察者模式是一个使用率非常高的模式,它最常用的地方就是GUI系统、订阅–发布系统。因为这个模式的一个重要作用就是解耦,将观察者和被观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。...
  • happy_horse
  • happy_horse
  • 2016年05月30日 21:05
  • 1837

设计模式之观察者模式 (Observer Design Pattern)

一、什么是观察者模式观察者定义了一种一对多的依赖关系,当一个主题(Subject)对象状态发生变化时,所有依赖它的相关对象都会得到通知并且能够自动更新自己的状态,这些依赖的对象称之为观察者(Obser...
  • suifeng3051
  • suifeng3051
  • 2016年04月27日 19:32
  • 939
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:iOS中的observer(观察者设计模式)swift版
举报原因:
原因补充:

(最多只允许输入30个字)