NSNotificationCenter存在的问题

通知没有统一的命名格式

对于通知的命名没有强制的要求,一个项目里可能有多种不同的命名规则。比如:

1
2
3
4
5
6
class Barista {
    let notification = "coffeeMadeNotification"
}
class Trainee {
    let coffeeMadeNotificationName = "Coffee Made"
}

通知名称可能冲突

因为对于通知名称没有限制,可能在不同的对象定义了同样的通知名,这样会导致难以意料的bug。

1
2
3
4
5
6
class Barista {
    let notification = "coffeeMadeNotification"
}
class Trainee {
    let coffeeMadeNotificationName = "coffeeMadeNotification"
}

通知的名称是字符串

字符串真的太危险了,很容易出现打错的情况。

1
2
NSNotificationCenter.defaultCenter()
    .postNotificationName("coffeeMadNotfication")

利用protocol的解决方案

我们通过设计一个protocol来解决上面提到的问题。

1
protocol Notifier { }

通过枚举统一通知名称

给这个协议增加一个关联类型:

1
2
3
protocol Notifier {
    associatedType Notification: RawRepresentable
}

所有要发送通知的对象或者结构体都要实现Notifier这个协议,然后提供一个实现了RawRepresentable的类型。其实就是一个字符串枚举。

1
2
3
4
5
6
class Barista : Notifier {
    enum Notification : String {
        case makingCoffee
        case coffeeMade
    }
}

这样就可以有一个统一的方式获取通知名称:

1
2
3
let coffeeMade = Barista.Notification.coffeeMade.rawValue
NSNotificationCenter.defaultCenter()
    .postNotificationName(coffeeMade)

避免通知名称冲突

我们可以为通知添加一个唯一的命名空间(namespace)来避免冲突。这里想到的解决方案是使用实现这个协议的object名称,因为每个object的名称在一个项目里是唯一的。

1
2
3
4
5
6
let baristaNotification =
    "\(Barista).\(Barista.Notification.coffeeMade.rawValue)"
let traineeNotification =
    "\(Trainee).\(Trainee.Notification.coffeeMade.rawValue)"
// baristaNotification: Barista.coffeeMade
// traineeNotification: Trainee.coffeeMade

但是每个通知都要手动添加就太蛋疼了。我们给这个协议加一个拓展方法来生成唯一的通知名称。因为这个方法只需要内部知道,所以标记为private。

1
2
3
4
5
public extension Notifier where Notification.RawValue == String {
    private static func nameFor(notification: Notification) -> String {
        return "\(self).\(notification.rawValue)"
    }
}

Notifier的扩展方法

添加观察者

最后一个通知的参数类型就是前面定义的那个枚举类型,这样就不用输入通知名称的字符串。

1
2
3
4
5
static func addObserver(observer: AnyObject, selector: Selector, notification: Notification) {
    let name = nameFor(notification)
    NSNotificationCenter.defaultCenter()
        .addObserver(observer, selector: selector, name: name, object: nil)
}

这样在使用的时候,在实现协议的object上直接方便的添加观察者:

1
Barista.addObserver(customer, selector: #selector(Customer.drink(_:)), notification: .coffeeMade)

发送通知

调用的时候应该是这样的:

1
Barista.postNotification(.coffeeMade)

这里利用了swfit的默认参数,object和userinfo设置一个默认的空值。实现如下:

1
2
3
4
5
static func postNotification(notification: Notification, object: AnyObject? = nil, userInfo: [String : AnyObject]? = nil) {
    let name = nameFor(notification)
    NSNotificationCenter.defaultCenter()
        .postNotificationName(name, object: object, userInfo: userInfo)
}

移除观察

这个实现就不贴了。和前面两个方法类似。调用的时候是这样的:

1
Barista.removeObserver(customer, notification: .coffeeMade)

总结

通 过灵活利用swift的语言特性:协议关联类型,协议可以添加默认的方法实现以及方法的默认参数,利用自定义的Notifier协议封装了 NSNotificationCenter的调用方式,解决了传统NSNotificationCenter调用的可能产生的三个潜在风险。

源码:https://github.com/andyyhope/Blog_NSNotificationCenterProtocol

Swift利用协议优化NSNotificationCenter的更多相关文章

  1. Swift泛型协议的N种用法

    They said "you should learn a new language every year," so I  learned Swift. Now  I  learn ...

  2. Android中利用ViewHolder优化自定义Adapter的典型写法

    利用ViewHolder优化自定义Adapter的典型写法 最近写Adapter写得多了,慢慢就熟悉了. 用ViewHolder,主要是进行一些性能优化,减少一些不必要的重复操作.(WXD同学教我的. ...

  3. 利用Gulp优化部署Web项目[长文慎入]

    Gulp Gulp是一款项目自动化的构建工具,与Grunt一样可以通过创建任务(Task)来帮助我们自动完成一些工作流的内容.当然,今天我们的内容并不是讨论这二者的区别,仅仅是介绍介绍如何利用Gulp ...

  4. 【转】利用TCMalloc优化Nginx的性能

    From: http://www.linuxidc.com/Linux/2013-04/83197.html TCMalloc的全称是 Thread-Caching Malloc,是谷歌开发的开源工具 ...

  5. Objective-C学习笔记 利用协议实现回调函数

    来源:http://mobile.51cto.com/iphone-278354.htm Objective-C学习笔记 利用协议实现回调函数是本文要介绍的内容,主要是实现一个显示文字为测试的视图,然 ...

  6. 利用Swift之协议语法实现页面间的传值功能

    随着Swift 新开发语言的发布,又随着Xcode6.0.1的正式发布,利用swift编写iOS代码迫在眉睫,笔者在使用Objective-C开发近三年以来,对这种优雅的语法深感赞叹,下面我将对比式的 ...

  7. 高速网络下的http协议优化

    http协议是基于TCP协议,具备TCP协议的所有功能.但是与一般TCP的长连接不同的是http协议往往连接时间比较短,一个请求一个响应了事.但是总所周知,TCP协议除了具备可靠的传输以外,还有拥塞控 ...

  8. 利用Select2优化@Html.ListBoxFor显示,学会用MultiSelectList

    最近需要用到多选框,Asp.Net MVC自带的@Html.ListBox或@Html.ListBoxFor的显示效果太差,于是找到了Select2进行优化,并正式了解了多选框的操作方法. 首先介绍多 ...

  9. 窥探Swift之协议(Protocol)和委托代理(Delegate)回调的使用

    协议与委托代理回调在之前的博客中也是经常提到和用到的在<Objective-C中的委托(代理)模式>和<iOS开发之窥探UICollectionViewController(四) - ...

随机推荐

  1. jstl

    今日内容 l JSTL标签库 l EL函数 l 自定义标签库开发 l JSP模式和案例(*****) 1.1 上次课内容总结 JSP技术: * JSP:Java Server Pages * JSP运 ...

  2. oracle中根据当前记录查询前一条和后一条记录

    select * from aa01_2014 where aaa001=(select c.p from (select aaa001,lag(aaa001,1,0)  over (order by ...

  3. jq表头固定

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta na ...

  4. LeetCode之412. Fizz Buzz

    -------------------------------------------- 虽然是从最简单的开始刷起,但木有想到LeetCode上也有这么水的题目啊... AC代码: public cl ...

  5. 日期与时间戳相互转化(js)

    function datetime_to_unix(datetime){     var tmp_datetime = datetime.replace(/:/g,'-');     tmp_date ...

  6. [译]:Orchard入门——使用标签管理内容

    原文链接:Organizing Content Using Tags 在Orchard中,内容项可以使用标签来分类,同时,通过格式为 ~/tags/tag-name 的url可以直接查看包含对应标签关 ...

  7. 初识ZooKeeper

    最近在看Apache下的一个开源项目ZooKeeper(http://zookeeper.apache.org/doc/trunk/),用百度百科介绍的来说,Zookeeper是一个针对大型分布式系统 ...

  8. [leetcode] 12. Integer to Roman

    关于罗马数字: I: 1V: 5X: 10L: 50C: 100D: 500M: 1000字母可以重复,但不超过三次,当需要超过三次时,用与下一位的组合表示:I: 1, II: 2, III: 3, ...

  9. Hdu 4081 最小生成树

    Qin Shi Huang's National Road System Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/3 ...

  10. C#-ASP.NET MVC-架构【1】-自定义错误页

    自定义异常本来是一件很简单的事情,没想到在做的过程中遇到各种坑,目前来说,还有Session过期和Ajax请求这两种情况没有特殊处理,其他的基本已经可以使用,等慢慢完善吧. 一.在web.config ...