Typed Notification Observers
文章翻译自objc,原文连接:『Typed Notification Observers』
这一节是建立在NSNotificationCenter之上的。我们要改善两点:第一,实现对notification center增加和移除观察的自动管理,使得添加观察的操作就像是创建一个对象;移除的操作就像是回收这个对象。第二,我们将应用前边讲到的Phantom Types来实现我们自己的通知类型。
首先,我们创建一个结构体来描述notifiction。注意,这个结构体有一个phantom type:A。A将是存储到notifiction的userInfo中的类型。结构体里只存粗了notifiction的名称:
struct Notification<A> {
let name: String
}
我们需要对notifiction的userInfo进行简单的包装。由于NSNotificationCenter的工作机制,我们需要对value进行包装,防止value不是对象类型:
func postNotification<A>(note: Notification<A>, value: A) {
let userInfo = ["value": Box(value)]
let center = NSNotificationCenter.defaultCenter()center.postNotificationName(note.name, object: nil, userInfo: userInfo)
}
现在,我们可以创建我们的观察对象了。创建一个该对象的实例,然后向NSNotificationCenter添加观察者,当它需要销毁的时候,从NSNotificationCenter移除观察。这样我们就可以把它当作一个对象的属性来持有,一但对象的该属性被置为nil(例如当该对象被dealloc时),它就会自动从NSNotificationCenter中移除(省去了频繁编码实现的困扰)。
class NotificationObserver {
let observer: NSObjectProtocol
init(notification: Notification<A>, block aBlock: A -> ()) {
let center = NSNotificationCenter.defaultCenter()
observer = center.addObserverForName(notification.name,
object: nil,
queue: nil) { note in
if let value = (note.userInfo?["value"] as? Box<A>)?.unbox {
aBlock(value)
}
}
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(observer)
}
}
我们有各种需要使用我们定义的notification的场景。例如我们需要一个持有NSError对象的globalPanicNotification:
let globalPanicNotification: Notification<NSError> = Notification(name: "Global panic")
直接调用postNotification:就可以发出通知:
let myError: NSError = NSError(domain: "io.objc.sample", code: 42, userInfo: [:])
postNotification(globalPanicNotification, myError)
需要注意的是,因为采用了phantom types,声明时是NSError类型,所以调用时也只能传递NSError类型的参数。对该类型的通知建立观察,我们只需要在我们的类里创建一个这样的实例变量:
let panicObserver = NotificationObserver(notification: globalPanicNotification) { err in
println(err.localizedDescription)
}
这就是Typed Notification。现在,我们有了一个类型安全的notification,并且不用担心添加和移除观察的逻辑,它可以自动完成这些功能。我们可以利用框架本身提供的各种奇妙的基础结构,进行简单包装,便可大大提升安全性。所有的源码都可以从这里获取。