iOS KVO (1)KVO基礎(chǔ)講解

KVO 簡述

KVO<NSKeyValueObserving>,是一個非正式協(xié)議,它定義了對象之間觀察和通知狀態(tài)改變的通用機制。

我們可以監(jiān)聽一個對象的屬性,包括簡單屬性,一對一的關(guān)系,和一對多的關(guān)系。一對多關(guān)系的監(jiān)聽者會被告知集合變更的類型,以及哪些對象參與了變化。

NSObject提供了一個NSKeyValueObserving協(xié)議的默認(rèn)實現(xiàn),它為所有對象提供了一種自動發(fā)送修改通知的能力。我們可以通過禁用自動發(fā)送通知并使用這個協(xié)議提供的方法來手動實現(xiàn)通知的發(fā)送,以便更精確地去處理通知。

基礎(chǔ)方法的講解

使用KVO必須要滿足的條件和一般使用步驟

1.該對象必須支持KVC(凡是繼承自NSObject的類都支持KVC)
2.作為觀察者的對象必須實現(xiàn) -(void)observeValueForKeyPath:ofObject:change:context: 方法
3.被觀察的對象要用- (void)addObserver:forKeyPath:options:context:方法注冊觀察者
4.用完要移除。附上方法- (void)removeObserver:forKeyPath: 或者- (void)removeObserver:forKeyPath:context:

然后我們對KVO方法進(jìn)行詳細(xì)的講解。

1.- (void)addObserver:forKeyPath:options:context:觀察對象的添加 以及被觀察者的確定

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;

解析:
1.此方法為減號方法 調(diào)用者即為被觀察者。

  1. observer 此參數(shù)為觀察者 也就是監(jiān)控者 它可以通過實現(xiàn)
    -(void)observeValueForKeyPath:ofObject:change:context:對被觀察者進(jìn)行觀察。

  2. forKeyPath 所觀察屬性的名字(宏觀看法,后續(xù)會詳解)

4.options 這個參數(shù)可以理解為所需觀察的設(shè)置選項

可以很清楚的看到,其中包含了四種值,分別為:
NSKeyValueObservingOptionNew:提供更改前的值
NSKeyValueObservingOptionOld:提供更改后的值
NSKeyValueObservingOptionInitial:觀察最初的值(在注冊觀察服務(wù)時會調(diào)用一次觸發(fā)方法)
NSKeyValueObservingOptionPrior:分別在值修改前后觸發(fā)方法(即一次修改有兩次觸發(fā))
稍后會對此進(jìn)行案例的分析。

2.-(void)observeValueForKeyPath:ofObject:change:context: 觀察者觀察方法的實現(xiàn)

- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context;
  1. keyPath 被觀察的屬性 上個方法中已經(jīng)講解 屬于同一個參數(shù)

  2. object被觀察者所屬的對象 也就是本官者本身

  3. change 這是一個字典,它包含了屬性被修改的一些信息。
    這個字典中包含的值會根據(jù)我們在添加觀察者時(addObserver方法)設(shè)置的options參數(shù)有所變化。

  4. context 添加觀察者時的上下文信息,它可以被用作區(qū)分那些綁定同一個keypath的不同對象的觀察者。
    比如說觀察一些繼承自同一個父類的子類,而這些子類都有一個相同的keyPath。
    context:(nullable void *)context;

關(guān)于方法講解 我們到此為止。我們接下來進(jìn)行案例分析

3- (void)removeObserver:forKeyPath: 或者- (void)removeObserver:forKeyPath:context:

- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

注意:
可能導(dǎo)致崩潰或內(nèi)容出錯的情況
1.被觀察的對象銷毀掉了(被觀察的對象是一個局部變量)
2.觀察者被釋放掉了,但是沒有移除監(jiān)聽(如模態(tài)推出,push,pop等)
2.注冊的監(jiān)聽沒有移除掉,又重新注冊了一遍監(jiān)聽

4 案例分析

1. Person.h

#import <Foundation/Foundation.h>

@interface Person : NSObject
@property (nonatomic,copy)NSString *age;

@end

2. Person.m

#import "Person.h"

@implementation Person

@end

3.調(diào)用 options

3.1 options ---> NSKeyValueObservingOptionNew
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic,strong)Person *person;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    //1.注冊觀察者,實施監(jiān)聽
    self.person = [[Person alloc]init];
    [self.person addObserver:self
                  forKeyPath:@"age"
                     options:NSKeyValueObservingOptionNew
                     context:nil];
    self.person.age = @"18";
}

- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context{
    NSLog(@"keyPath:%@",keyPath);
    NSLog(@"object:%@",object);
    NSLog(@"change:%@",change);
    
}


打印數(shù)據(jù):

2018-05-21 15:32:11.792849+0700 KVODemo[51410:1650569] keyPath:age
2018-05-21 15:32:11.793007+0700 KVODemo[51410:1650569] object:<Person: 0x600000011660>
2018-05-21 15:32:11.793198+0700 KVODemo[51410:1650569] change:{
    kind = 1;
    new = 18;
}
3.2 options ---> NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld

打印數(shù)據(jù):

2018-05-21 15:35:28.030530+0700 KVODemo[51467:1652953] keyPath:age
2018-05-21 15:35:28.030690+0700 KVODemo[51467:1652953] object:<Person: 0x604000017fd0>
2018-05-21 15:35:28.030885+0700 KVODemo[51467:1652953] change:{
    kind = 1;
    new = 18;
    old = "<null>";
}
3.3 options ---> NSKeyValueObservingOptionInitial
2018-05-21 15:42:56.834653+0700 KVODemo[51666:1660465] keyPath:age
2018-05-21 15:42:56.834798+0700 KVODemo[51666:1660465] object:<Person: 0x604000205260>
2018-05-21 15:42:56.834965+0700 KVODemo[51666:1660465] change:{
    kind = 1;
}
2018-05-21 15:42:56.835267+0700 KVODemo[51666:1660465] keyPath:age
2018-05-21 15:42:56.835364+0700 KVODemo[51666:1660465] object:<Person: 0x604000205260>
2018-05-21 15:42:56.835492+0700 KVODemo[51666:1660465] change:{
    kind = 1;
}

總結(jié):我們發(fā)現(xiàn)當(dāng)options 是 NSKeyValueObservingOptionInitial監(jiān)聽方法只在注冊觀察者者的時候 調(diào)用了一次 所以此方法一般用的不多,可以用作初始值的獲取。

3.4 options ---> NSKeyValueObservingOptionPrior
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic,strong)Person *person;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    //1.注冊觀察者,實施監(jiān)聽
    
    self.person = [[Person alloc]init];
    [self.person addObserver:self
                  forKeyPath:@"age"
                     options:NSKeyValueObservingOptionPrior
                     context:nil];
    self.person.age = @"18";
    self.person.age = @"19";
    
}

- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context{
    NSLog(@"keyPath:%@",keyPath);
    NSLog(@"object:%@",object);
    NSLog(@"change:%@",change);
    
}
打印數(shù)據(jù):
2018-05-21 15:48:51.106960+0700 KVODemo[51814:1666067] keyPath:age
2018-05-21 15:48:51.107172+0700 KVODemo[51814:1666067] object:<Person: 0x60000000ba00>
2018-05-21 15:48:51.107394+0700 KVODemo[51814:1666067] change:{
    kind = 1;
    notificationIsPrior = 1;
}
2018-05-21 15:48:51.107520+0700 KVODemo[51814:1666067] keyPath:age
2018-05-21 15:48:51.107615+0700 KVODemo[51814:1666067] object:<Person: 0x60000000ba00>
2018-05-21 15:48:51.107734+0700 KVODemo[51814:1666067] change:{
    kind = 1;
}
2018-05-21 15:48:51.107831+0700 KVODemo[51814:1666067] keyPath:age
2018-05-21 15:48:51.107922+0700 KVODemo[51814:1666067] object:<Person: 0x60000000ba00>
2018-05-21 15:48:51.108045+0700 KVODemo[51814:1666067] change:{
    kind = 1;
    notificationIsPrior = 1;
}
2018-05-21 15:48:51.108123+0700 KVODemo[51814:1666067] keyPath:age
2018-05-21 15:48:51.108284+0700 KVODemo[51814:1666067] object:<Person: 0x60000000ba00>
2018-05-21 15:48:51.108414+0700 KVODemo[51814:1666067] change:{
    kind = 1;
}

總結(jié):
我們發(fā)現(xiàn)我們進(jìn)行兩次賦值,但是卻調(diào)用了四次,正如官方文檔解釋一般 它會在 調(diào)用之前調(diào)用之后 分別調(diào)用一次。

未完,待續(xù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時...
    歐辰_OSR閱讀 30,246評論 8 265
  • KVC KVC定義 KVC(Key-value coding)鍵值編碼,就是指iOS的開發(fā)中,可以允許開發(fā)者通過K...
    暮年古稀ZC閱讀 2,292評論 2 9
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,067評論 0 9
  • KVO 作為 iOS 中一種強大并且有效的機制,為 iOS 開發(fā)者們提供了很多的便利;我們可以使用 KVO 來檢測...
    JzRo閱讀 1,079評論 0 2
  • 首先執(zhí)行 cp /usr/share/vim/vimrc ~/.vimrc 拷貝默認(rèn)的配置文件 接著vi...
    moyacc閱讀 415評論 0 0

友情鏈接更多精彩內(nèi)容