Swift小技巧通知转协议,让你再也不用通知做跨层传递

前言

iOS开发中,如果我们需要做页面的跨层级传递,比如A-B-C三层界面,想要C传递到A如果不想写多次闭包或者代理,那么我们一般会用NSNotification做传递,也就是通知。通知可以一对多,只要添加了观察者,然后在需要传递的地方postNotification就可以收到通知了,非常的方便。但是它也有它不好的地方,iOS9之前通知观察了因为是强引用,导致如果不调用removeObserver会内存泄露,当然现在已经不存在这个问题了。另外多次addObserver会接受到多次方法,导致代码可能出现问题,还有最后一个老生常谈的问题,如果Key写错了也容易导致出错。那么有没有一种方法能解决跨层传递,又不需要用到Notfication呢。答案是有的,我们自己封装一个MessageCenter类,用协议来传递不就可以了?下面我们说干就干。

实现过程

  • 定义一个消息协议

我们期望兼容OC,所以使用NSObjectProtocol。

less 复制代码
@objc protocol JFMessageProtocol: NSObjectProtocol {

@objc optional func messageProtocol_testMethod()

}
  • 定义消息中心类

定义消息中心类,内部的pushClients是一个弱引用的哈希Map用来装观察者,这样就无需担心没有remove观察者导致的内存泄露问题。obs方法则和通知的addObserver方法是一样的,用于添加观察者。sendMessage方法和postNotification方法类似,但是这里发送的是协议方法,这样这样添加了观察的类并且实现了该协议方法,就可以完成一次消息传递啦。

就这短短的20行代码,我们就从此可以丢掉通知了,改用协议做跨层传递。

swift 复制代码
class JFMessageCenter: NSObject {

@objc dynamic static let shared = JFMessageCenter()

private var pushClients = NSHashTable<AnyObject>.weakObjects()

@objc func obs(with target: AnyObject) {

self.pushClients.add(target)

}

//因为是弱引用,不手动调用也没事

@objc func removeObs(with target: AnyObject) {

self.pushClients.remove(target)

}

@objc public func sendMessage(selector: Selector ,completion: @escaping (JFMessageProtocol) -> Void) {

for client in self.pushClients.allObjects {

if client.responds(to: selector), let client = client as? JFMessageProtocol {

print("receive message")

if Thread.current != .main {

DispatchQueue.main.async {

completion(client)

}

} else {

completion(client)

}

}

}

}

}

使用方法

在需要观察的地方观察,并实现要调用的协议方法

scss 复制代码
override func viewDidLoad() {
    super.viewDidLoad()
    JFMessageCenter.shared.obs(with: self)
}

extension ViewController: JFMessageProtocol {
    func messageProtocol_testMethod() {
        JFPopup.toast {
            [                .hit("我是通知转协议"),                .position(.center)            ]
        }
//        JFPopup.toast(hit: "我是通知转协议")
        print("我是通知转协议")
        print(Thread.current)
    }
}

在想发送消息的地方,发送消息即可,非常方便

javascript 复制代码
JFMessageCenter.shared.sendMessage(selector: #selector(JFMessageProtocol.messageProtocol_testMethod)) { proto in

proto.messageProtocol_testMethod?()

}

是否要加锁的问题

上面我写的类是没有加锁的,考虑我自身是用来做页面的跨层传递,一般都是在控制器或者视图层的初始化里添加观察,都是主线程,必然优先观察到,所以就没有考虑加锁的问题。这里仅提供一个思路,如果想用该方法同学想严谨一点,可以自行补上。

测试Demo地址

MessageProtocolDemo

结尾

以上就是我说的Swift小技巧,如有说得不对的地方请多包涵,欢迎互相讨论🤚

相关推荐
2501_916007477 小时前
抓包工具功能对比,TraceEagle、Charles、Fiddler、Wireshark、mitmproxy 差别
前端·网络协议·ios·adb·https·fiddler·wireshark
青峰讲运维10 小时前
OpenStack 存储深度解析|Cinder 块存储 + Swift 对象存储原理与完整实操(Yoga)
开发语言·openstack·swift
2501_9151063212 小时前
Windows 上开发 Unity iOS,使用 KXBuild 进行真机调试、性能采样与打包 ipa
windows·vscode·unity·ios·个人开发·swift·敏捷流程
柚鸥ASO优化14 小时前
iOS 27.2 更新后,App Store 广告展示逻辑出现新变化
ios
美狐美颜SDK开放平台15 小时前
视频美颜SDK如何应对快速转头与大幅度面部动作?
android·人工智能·ios·音视频·直播美颜sdk·视频美颜sdk
茶底世界之下15 小时前
秋叶已经够黄了:用 Awaking 单独收一点黄色
ios
Jewel10516 小时前
文字为什么总是不居中!记录一个bug
android·flutter·ios
老李IT笔记17 小时前
抹除和升系统可以合成一条命令了:强制更新夹在抹除里怎么用,什么情况下不该用
ios·智能手机
zhongben123_2 天前
从零开发一个打车APP:技术选型、系统架构与成本评估指南
大数据·运维·ios·系统架构·uni-app·web app
茶底世界之下2 天前
人像清晰度加多少?用 Awaking 对照皮肤与发丝
ios