Swift 5 + iOS 13 以后 通知的监听与发送 NotificationCenter &[weak self]

1,998 阅读2分钟

使用通知的情况:

如果中间间隔多层视图,想要传递消息就使用通知(跳过多层传递)


使用通知来切换跟控制器

  • 注册通知:

// 注册通知 - 如果使用 通知中心的 block 监听,其中的 self 一定要 弱引用!

//全局通知定义
let WBSwitchRootViewControllerNotification = "WBSwitchRootViewControllerNotification"

在SceneDelegate

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) 方法里面

        // 监听通知
        //控制中心 是单例
        //[weak self] 控制中心 delegate 都是常驻,两个常驻最好写一个weak
        // 通知名称,通知中心用来识别通知的
        // 发送通知的对象,如果为nil,监听任何对象
        // nil,主线程
        NotificationCenter.default.addObserver(forName: NSNotification.Name.init(rawValue: WBSwitchRootViewControllerNotification), object: nil, queue: nil) { [weak self] notification in
        print(Thread.current)
        //通知的名称
        print(notification)
           
        //切换控制器
        self?.window?.rootViewController = MainViewController()
        }

在方法外面写 注销通知方法:可以不写(为了代码规范

// 注销通知 - 注销指定的通知
    deinit {
        NotificationCenter.default.removeObserver(self,   // 监听者
        name:NSNotification.Name.init(rawValue: WBSwitchRootViewControllerNotification) ,           // 监听的通知
            object: nil)                                   // 发送通知的对象
    }

  • 发送通知:

在需要调用的地方

//发送通知
NotificationCenter.default.post(name:NSNotification.Name.init(rawValue: WBSwitchRootViewControllerNotification) , object: nil)

[weak self]:

另一个例子: 在HomeTableViewController.swift里面:

NotificationCenter.default.addObserver(forName: NSNotification.Name.init(rawValue: WBStatusSelectedPhotoNotification), object: nil, queue: nil) {  [weak self] (n) in
            print("接收通知 \(n)")
            print(self?.view ?? "")
        }
  • 通知中心是进程,始终和程序在一起,不会被销毁

  • 通知中心是单例,始终存在,不会被销毁

  • print(self?.view ?? "") 通知对self有强引用

通知只要监听到字符串,就会执行 block,一直对self进行强引用,导致self不能被释放掉

给self发送通知,如果block里面有self,就一定会对self进行强引用,所以一般都会写[weak self]

  • 加了[weak self] 之后,block里面写self就要self?

因为不知道什么时候self就被释放掉了


代码 测试循环引用问题:测试HomeTableViewController.swift是否有循环引用问题

在SceneDelegate文件中

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)里面

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = (scene as? UIWindowScene) else { return }
        let newWindow = UIWindow(windowScene: windowScene)
        // 测试通知中心引用代码
        let vc = UIViewController()
        let nav = UINavigationController(rootViewController: vc)
        vc.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "测试", style: .plain, target: self, action: #selector(self.testClick))
        newWindow.rootViewController = nav
        newWindow.makeKeyAndVisible()

在SceneDelegate文件中

@objc func testClick() {
        // push home
        let nav = window?.rootViewController as! UINavigationController
        nav.pushViewController(HomeTableViewController(), animated: true)
    }

设置断点:

在HomeTableViewController.swift

deinit {
        // 注销通知
        NotificationCenter.default.removeObserver(self)
    }

开始测试:

image.png

点击测试

image.png

点击back

如果断点卡住说明成功释放:

image.png