从 Notification.Name 看 Swift 如何优雅的解决 String 硬编码

栏目: Swift · 发布时间: 6年前

内容简介:初学 Swift 中相关 NSNotification 的代码时, 发现了之前熟悉的 name 参数的类型由 Objective-C 中的 NSString 变成了 Notification.Name 类型. 并不是我期望的 String 类型...这是怎么回事呢?那么, 在 Swift 中如何使用 Notification 呢, 以 post 为例.其中,

初学 Swift 中相关 NSNotification 的代码时, 发现了之前熟悉的 name 参数的类型由 Objective-C 中的 NSString 变成了 Notification.Name 类型. 并不是我期望的 String 类型...这是怎么回事呢?

Swift 中如何使用 Notification

那么, 在 Swift 中如何使用 Notification 呢, 以 post 为例.

NotificationCenter.default.post(name: Notification.Name.UIApplicationDidFinishLaunching, object: nil)
复制代码

其中, Notification.Name 是可以省略的, 就变为了

NotificationCenter.default.post(name: .UIApplicationDidFinishLaunching, object: nil)
复制代码

查看定义发现了 UIApplicationDidFinishLaunching 实际上是定义在结构体 NSNotification.Name 扩展(extension)中的的一个静态常量 ( static let ), 类型是 NSNotification.Name

extension NSNotification.Name {

    @available(iOS 4.0, *)
    public static let UIApplicationDidEnterBackground: NSNotification.Name

    @available(iOS 4.0, *)
    public static let UIApplicationWillEnterForeground: NSNotification.Name

    public static let UIApplicationDidFinishLaunching: NSNotification.Name
    
    ...
}
复制代码

所以我们才可以省略前面的 Notification.Name 直接使用 .UIApplicationDidFinishLaunching (Notification.Name 是 NSNotification.Name 的别名)

那我们如果想自定义一个通知怎么办呢, 直接可以仿照系统的方式, 我们自己为其增加一个 extension

extension Notification.Name {
	static let LoginStatusChanged = Notification.Name("LoginStatusChanged")
}
复制代码

其中 Notification.Name("LoginStatusChanged") 是其初始化方法, 可查看文档说明, 使用时, 可直接

NotificationCenter.default.post(name: .LoginStatusChanged, object: nil)
复制代码

因为这个通知 LoginStatusChanged 是定义在 Notification.Name 中的了, 所以也没必要在名称后面增加 Notification 等字样来表示这是一个通知了. 所以 Swift 中很多定义的名称都是非常简洁的.

对比 Objective-C 中的使用

对比之前在 Objective-C 中的使用

[[NSNotificationCenter defaultCenter] postNotificationName:"xxxxxxxxxx" object:nil

这样是非常容易出错的, 查这样的错误经常也是非常费时费力的, 也让人看来是非常不优雅的, 所以我们经常会进行宏定义或者是常量来防止字符串硬编码的问题.

但这实际上也是会带来一些令人头疼的问题的:

  1. 为了表明定义的字符串常量是一个通知名, 还要为其增加冗长的前缀或者是后缀
  2. 在开发中还经常会在代码补全中, 看到根本不和场合的一些常量名
  3. 通常为了使用方便和易于维护, 还会在将所有的通知定义在一个 xxDefine.h 的头文件中, 并在 pch 文件中引用, 此时如果增删或者修改了任意通知. 将会引起工程的全量重新编译. 也很是头疼. ...

所以, Swift 这种使用方式可谓是十分优雅.

举一反三

在开发中, 其实类似于 Notification 这种需要传递字符串的场景还有很多, 我们都可以使用这类使用方法进行优化.

场景

假设有这样一个场景, 定义一个类 EventReporter 用来处理埋点请求.

class EventReporter {

	static let shared = EventReporter()

	func reportEvent(_ eventId: String, withParams params: [String:Any]?) {
		// 埋点上报逻辑
	}
}
复制代码

相信这样的场景是很多人都见过的, 其中 eventId 是我们埋点的事件的ID, 那么该如何使用类似 Notification.Name 的方式来优化这类场景呢?

原理

从文档中看出 Notification.Name 实际上是遵从了一个协议 RawRepresentable

Overview
With a RawRepresentable type, you can switch back and forth between a custom type and an associated RawValue type without losing the value of the original RawRepresentable type. Using the raw value of a conforming type streamlines interoperation with Objective-C and legacy APIs and simplifies conformance to other protocols, such as Equatable, Comparable, and Hashable.
The RawRepresentable protocol is seen mainly in two categories of types: enumerations with raw value types and option sets.

简单的说就是, 使用 RawRepresentable 类型, 可以在自定义类型和其关联的 RawValue 类型之间来回切换, 可简化与 Objective-C 和传统 API 的交互, 两类:具有原始值类型和选项集的枚举(OptionSet, 其实 Swift 中的选项集枚举就是集成自 RawRepresentable 这个 Protocol 实现的), 说白了. 就是用一个类型封装一下我们想要使用的类型比如说 String, 来方便交互.

实现

使用起来很简单, 定义一个结构体来管理所有的埋点事件

struct EventID: RawRepresentable {
	
}
复制代码

根据编译器提示, 补全协议代码

struct EventID: RawRepresentable {
	typealias RawValue = String
	
	var rawValue: String
	
	init?(rawValue: String) {
		
	}
}
复制代码

从这就更容易看出其原理, 实际上内部的 rawValue 属性就是我们需要使用的 String 类型的事件名, 初始化方法传入该 String 对其赋值即可, 返回 EventID 类型的结构体

这里发现初始化方法返回的是一个 Optional 类型, 这样使用起来还需要解包, 不太方便, 可以看到 Notification.Name 的初始化方法返回并不是 Optional , 因为定义都是非常确定的事件名(通知名), 而且 init 方法中也不会产生异常, 所以此处没什么必要使用 Optional , 去掉 ? 即可

struct EventID: RawRepresentable {
	typealias RawValue = String
	
	var rawValue: String
	
	init(rawValue: String) {
		self.rawValue = rawValue
	}
}
复制代码

那么, 我们的上报类的代码可以修改如下, 这里还可以给 params 一个默认值, 这样如果没有参数时, 可以只传递 eventId 一个参数即可.

class EventReporter {

	static let shared = EventReporter()

	func reportEvent(_ eventId: EventID, withParams params: [String:Any]? = nil) {
		let event = eventId.rawValue
		// 埋点逻辑
	}
}
复制代码

最后, 定义一个埋点事件看看吧~, 推荐写到 extension 中易于维护.

extension EventID {
	static let LoginPageExposure = EventID(rawValue: "login_page_exposure")
}
复制代码

那么使用的时候,

EventReporter.shared.reportEvent(.LoginPageExposure)
复制代码

当我们打出 . 的时候, 代码补全就已经将 LoginPageExposure 提示给我们了.


以上所述就是小编给大家介绍的《从 Notification.Name 看 Swift 如何优雅的解决 String 硬编码》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

UNIX网络编程 卷2

UNIX网络编程 卷2

W.Richard Stevens / 人民邮电出版社 / 2009-11 / 89.00元

《UNIX网络编程 卷2:进程间通信(英文版·第2版)》是一部UNIX网络编程的经典之作。进程间通信(IPC)几乎是所有Unix程序性能的关键,理解IPC也是理解如何开发不同主机间网络应用程序的必要条件。《UNIX网络编程 卷2:进程间通信(英文版·第2版)》从对Posix IPC和System V IPC的内部结构开始讨论,全面深入地介绍了4种IPC形式:消息传递(管道、FIFO、消息队列)、同......一起来看看 《UNIX网络编程 卷2》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具