一、通知
1、發(fā)送通知
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、發(fā)送通知
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "headItemClick"), object: nil, userInfo: ["indexRow": String(indexPath.row)])
}
2、接受通知,并取出數(shù)據(jù)
ViewDidLoad中調(diào)用:
NotificationCenter.default.addObserver(self, selector: #selector(headItemClick(notif:)), name: NSNotification.Name(rawValue: "headItemClick"), object: nil)
extension ZJDiscoverViewController{
@objc func headItemClick(notif:NSNotification) {
//轉(zhuǎn)成int類型
let index = (notif.userInfo!["indexRow"] as? String)!
let row = Int(index)
print("已經(jīng)把主要點(diǎn)擊的數(shù)據(jù)傳遞到VC中來(lái)了:",row!)
}
}
3、移除通知
deinit方法,相當(dāng)于OC的delloc
deinit {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: "headItemClick"), object: nil)
}
3、響應(yīng)事件
@objc func tongzhiwo() {
print("餓哦們接收到了通知哦")
}
4、注銷通知:在注冊(cè)通知的頁(yè)面加上
deinit {
NotificationCenter.default.removeObserver(self)
}
二、代理
1、在NextViewController中創(chuàng)建代理
@objc protocol nextVCDelegate{
func nextVCTextFieldValue(value: String?)
}
2、聲明代理
weak var delegate : nextVCDelegate?
3、在HomeViewController中調(diào)用代理
viewDidLoad中調(diào)用方法:
let nextVC = NextViewController()
nextVC.delegate = self
extension HomeViewController : nextVCDelegate{
func nextVCTextFieldValue(value: String?) {
print("調(diào)用了代理",value as Any)
}
}
協(xié)議的繼承:
a、secondNextVCDelegate繼承協(xié)議nextVCDelegate,這樣secondNextVCDelegate就擁有nextVCDelegate的協(xié)議nextVCTextFieldValue
b、Swift的協(xié)議要求都是必須實(shí)現(xiàn)的,所以如果我們想要協(xié)議變成可選的,那么就要使用調(diào)用OC的功能,在方法前添加@objc,同時(shí)也添加optional表示為可選。
@objc protocol nextVCDelegate{
func nextVCTextFieldValue(value: String?)
}
@objc protocol secondNextVCDelegate : nextVCDelegate {
@objc optional func breath(value: String)
}
三、KVO
1、為什么修改時(shí)屬性要用@objc 和 dynamic呢?
首先我們了解一下swift是靜態(tài)語(yǔ)言,并沒(méi)有OC的動(dòng)態(tài)分發(fā)機(jī)制。
- 而KVO的本質(zhì)是基于runtime的動(dòng)態(tài)分發(fā)機(jī)制,通過(guò)key來(lái)監(jiān)聽(tīng)Value的值。
- OC能夠?qū)崿F(xiàn)監(jiān)聽(tīng)因?yàn)槎甲袷亓薔SKeyValueCoding協(xié)議。
- OC所有的類都是繼承自NSObject,其默認(rèn)已經(jīng)遵守了該協(xié)議,但Swift不是基于runtime的,
Swift 中的屬性處于性能等方面的考慮默認(rèn)是關(guān)閉動(dòng)態(tài)分發(fā)的,只有在屬性前加 dynamic才會(huì)開啟運(yùn)行時(shí),允許監(jiān)聽(tīng)屬性的變化。 - 添加@objc是讓屬性擁有OC的屬性。
a.普通的監(jiān)聽(tīng)方式
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)
}
}
方法調(diào)用:
let dog = Dog()
dog.name = "zhang"
打印結(jié)果:
willSet zhang
Just changed from kai to zhang
b.RXSwift的監(jiān)聽(tīng)方式
extension ViewController{
//1、設(shè)置監(jiān)聽(tīng)的屬性。
class Dog: NSObject {
@objc dynamic var name : String?
}
//2、這里是RXSwift的監(jiān)控方式。
func userKVO() {
let dog = Dog()
dog.name = "zhang2112"
dog.rx.observe(String.self, "name").subscribe { name in
print("KVO監(jiān)控的名字為:\(name)")
}.disposed(by: bag)
dog.name = "zhang"
dog.name = "wang"
}
}
我們觀察可以發(fā)現(xiàn),使用swift的方法設(shè)置KVO監(jiān)控,需要重寫observe方法,并在里面設(shè)置監(jiān)聽(tīng)的屬性,而使用RxSwift方法則直接設(shè)置監(jiān)聽(tīng)即可,調(diào)用起來(lái)更加方便。
重要知識(shí)點(diǎn):
RxCocoa 提供了 2 個(gè)可觀察序列 rx.observe 和 rx.observeWeakly,它們都是對(duì) KVO 機(jī)制的封裝,二者的區(qū)別如下。
1、性能比較
- rx.observe 更加高效,因?yàn)樗且粋€(gè) KVO 機(jī)制的簡(jiǎn)單封裝。
- rx.observeWeakly 執(zhí)行效率要低一些,因?yàn)樗幚韺?duì)象的釋放防止弱引用(對(duì)象的 dealloc 關(guān)系)。
2、應(yīng)用場(chǎng)景比較
- 在可以使用 rx.observe 的地方都可以使用 rx.observeWeakly。
- 使用 rx.observe 時(shí)路徑只能包括 strong 屬性,否則就會(huì)有系統(tǒng)崩潰的風(fēng)險(xiǎn)。而 rx.observeWeakly 可以用在 weak 屬性上。
class ViewController: UIViewController {
let disposeBag = DisposeBag()
@objc dynamic var message = "hangge.com"
override func viewDidLoad() {
super.viewDidLoad()
//定時(shí)器(1秒執(zhí)行一次)
Observable<Int>.interval(1, scheduler: MainScheduler.instance)
.subscribe(onNext: { [unowned self] _ in
//每次給字符串尾部添加一個(gè)感嘆號(hào)
self.message.append("!")
}).disposed(by: disposeBag)
//監(jiān)聽(tīng)message變量的變化
_ = self.rx.observeWeakly(String.self, "message")
.subscribe(onNext: { (value) in
print(value ?? "")
})
}
}
四、閉包傳值(OC的block)
場(chǎng)景:自定義的UIView,里面有多個(gè)按鈕,我們需要監(jiān)控各個(gè)按鈕的點(diǎn)擊,并且為點(diǎn)擊的按鈕傳值,這時(shí)我們就需要OC中的block也就是:
1、定義一個(gè)閉包
typealias shopBtnClickBlock = (_ tag:Int) ->Void
2、聲明閉包的類型
var btnClickblock : shopBtnClickBlock?
3、實(shí)現(xiàn)閉包,由于要在其它類中調(diào)用,所以需要添加@escaping函數(shù)。
//Mark: - 購(gòu)買等按鈕點(diǎn)擊事件
@objc func gridBtnClick(button:UIButton){
btnClickblock?(button.tag)
}
4、使用
@IBAction func dianZan(_ sender: Any) {
if callBack != nil {
callBack!("點(diǎn)贊")
}
print("點(diǎn)贊")
}
5、在VC中調(diào)用
view.btnClickblock = {str in
print("打印出來(lái)block內(nèi)容為:\(str)")
}