swift的通知、代理、KVO&RXSwift键值观察、闭包(block)

一、通知

1、发送通知

NotificationCenter.default.post(name: NSNotification.Name(rawValue: "zhangkaikai"), object: nil, userInfo: nil)

2、接收通知

NotificationCenter.default.addObserver(self, selector: #selector(tongzhiwo), name: NSNotification.Name(rawValue: "zhangkaikai"), object: nil)

思考:如果我们需要通知传值呢?
1、发送通知

 func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
     NotificationCenter.default.post(name: NSNotification.Name(rawValue: "headItemClick"), object: nil, userInfo: ["indexRow": String(indexPath.row)])
}

2、接受通知,并取出数据

ViewDidLoad中调用:
NotificationCenter.default.addObserver(self, selector: #selector(headItemClick(notif:)), name: NSNotification.Name(rawValue: "headItemClick"), object: nil)

extension ZJDiscoverViewController{
   @objc func headItemClick(notif:NSNotification) {
    //转成int类型
    let index = (notif.userInfo!["indexRow"] as? String)!
    let row = Int(index)
    print("已经把主要点击的数据传递到VC中来了:",row!)
    }
}

3、移除通知
deinit方法,相当于OC的delloc

deinit {
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: "headItemClick"), object: nil)
       }

3、响应事件

 @objc func tongzhiwo()  {
       print("饿哦们接收到了通知哦")
 }

4、注销通知:在注册通知的页面加上

deinit {
    NotificationCenter.default.removeObserver(self)
}

二、代理

1、在NextViewController中创建代理

@objc protocol nextVCDelegate{
      func nextVCTextFieldValue(value: String?)
}

2、声明代理

 weak var delegate : nextVCDelegate?

3、在HomeViewController中调用代理

  viewDidLoad中调用方法:
  let nextVC = NextViewController()
  nextVC.delegate = self

extension HomeViewController : nextVCDelegate{
    func nextVCTextFieldValue(value: String?) {
        print("调用了代理",value as Any)
    }
}

协议的继承:
a、secondNextVCDelegate继承协议nextVCDelegate,这样secondNextVCDelegate就拥有nextVCDelegate的协议nextVCTextFieldValue
b、Swift的协议要求都是必须实现的,所以如果我们想要协议变成可选的,那么就要使用调用OC的功能,在方法前添加@objc,同时也添加optional表示为可选。

@objc protocol nextVCDelegate{
    func nextVCTextFieldValue(value: String?)
}

@objc protocol secondNextVCDelegate : nextVCDelegate {
    @objc optional func breath(value: String)
}

三、KVO

1、为什么修改时属性要用@objc 和 dynamic呢?

首先我们了解一下swift是静态语言,并没有OC的动态分发机制。

  • 而KVO的本质是基于runtime的动态分发机制,通过key来监听Value的值。
  • OC能够实现监听因为都遵守了NSKeyValueCoding协议。
  • OC所有的类都是继承自NSObject,其默认已经遵守了该协议,但Swift不是基于runtime的, Swift 中的属性处于性能等方面的考虑默认是关闭动态分发的,只有在属性前加 dynamic才会开启运行时,允许监听属性的变化。
  • 添加@objc是让属性拥有OC的属性。
a.普通的监听方式
  class Dog: NSObject {
        @objc dynamic var name = "kai"{
            willSet(newName){
                print("willSet",newName)
            }
            didSet(oldName){
                print("Just changed from \(oldName) to \(self.name)")
            }
         }
    }
//重写chongobserve方法
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == "name" {
            print("~~~~age被改变了", dog.name)
            print(change as Any)
           }
    }

方法调用:

let dog = Dog()
dog.name = "zhang"

打印结果:

willSet zhang
Just changed from kai to zhang
b.RXSwift的监听方式
extension ViewController{
    //1、设置监听的属性。
    class Dog: NSObject {
        @objc dynamic var name : String?
    }
    
    //2、这里是RXSwift的监控方式。
    func userKVO() {
        let dog = Dog()
        dog.name = "zhang2112"
        dog.rx.observe(String.self, "name").subscribe { name  in
            print("KVO监控的名字为:\(name)")
        }.disposed(by: bag)
        
        dog.name = "zhang"
        dog.name = "wang"
    }
}

我们观察可以发现,使用swift的方法设置KVO监控,需要重写observe方法,并在里面设置监听的属性,而使用RxSwift方法则直接设置监听即可,调用起来更加方便。


重要知识点:

RxCocoa 提供了 2 个可观察序列 rx.observe 和 rx.observeWeakly,它们都是对 KVO 机制的封装,二者的区别如下。

1、性能比较
  • rx.observe 更加高效,因为它是一个 KVO 机制的简单封装。
  • rx.observeWeakly 执行效率要低一些,因为它要处理对象的释放防止弱引用(对象的 dealloc 关系)。
2、应用场景比较
  • 在可以使用 rx.observe 的地方都可以使用 rx.observeWeakly。
  • 使用 rx.observe 时路径只能包括 strong 属性,否则就会有系统崩溃的风险。而 rx.observeWeakly 可以用在 weak 属性上。
class ViewController: UIViewController {
     
    let disposeBag = DisposeBag()
     
    @objc dynamic var message = "hangge.com"
     
    override func viewDidLoad() {
        super.viewDidLoad()
       
        //定时器(1秒执行一次)
        Observable.interval(1, scheduler: MainScheduler.instance)
            .subscribe(onNext: { [unowned self] _ in
                //每次给字符串尾部添加一个感叹号
                self.message.append("!")
            }).disposed(by: disposeBag)
         
        //监听message变量的变化
        _ = self.rx.observeWeakly(String.self, "message")
            .subscribe(onNext: { (value) in
            print(value ?? "")
        })
    }
}

四、闭包传值(OC的block)

场景:自定义的UIView,里面有多个按钮,我们需要监控各个按钮的点击,并且为点击的按钮传值,这时我们就需要OC中的block也就是:

1、定义一个闭包

typealias swiftBlock = (_ str: String) -> Void

2、声明闭包的类型

var callBack : swiftBlock?

3、实现闭包,由于要在其它类中调用,所以需要添加@escaping函数。

func callBackBlock(_ block: @escaping swiftBlock) {
     callBack = block
}

4、使用

 @IBAction func dianZan(_ sender: Any) {
        if callBack != nil {
            callBack!("点赞")
        }
        print("点赞")
 }

5、在VC中调用

override func layoutSubviews() {
    super.layoutSubviews()
    loadUI()
    footView.callBackBlock { (str) in
        print("~~~~~~~传过来的参数:\(str)")
    }     
}

你可能感兴趣的:(swift的通知、代理、KVO&RXSwift键值观察、闭包(block))