? ? KVO(key-value Observer),通過命名可以聯想到,一個監視著監視著鍵值配對,讓一個對象A來監視另一個對象B中的鍵值,一旦B中的受監視鍵所對應的值發生了變化,對象A會進入一個回調函數,有機會對于B中的受監視鍵值的改變立刻進行處理和應對。
? ? 注:雖然對象A中的回調函數有點像代理方法,但是回調函數的調用和鍵值發生變化處在同一個線程中,并非像某些代理方法會在另一個線程中進行回調。也就是說,如果對鍵key進行了監視,一旦鍵key對應的值發生了變化,就會去調用監視著的回調函數,直到回調函數跑完后鍵key對應值發生變化的流程才能繼續。
? ? 好處就是減少膠水代碼。
? ? 比如比賽比分發生了變化,如果我們不用KVO機制,我們需要告訴大屏幕控制人員,告訴網絡媒體,告訴廣播電臺播音員,甚至告訴其他賽場的工作人員。
??
一個簡單的KVO機制的程序
導航欄有三個元素,左邊的編輯按鈕,用來刪除表的記錄,右邊的“+”按鈕,用來新增表的記錄,而當中的標題,用來顯示最近的一次動作。開發思路大致為這樣:
? ?表視圖有一個數據源dataSource,我們需要利用kVO機制去監視這個數據源,當按下“+”按鈕時往數據源中添加一條數據,觸發KVO,隨后在KVO的回調函數中,我們將界面更新成和數據源同步。
? ?當刪除一條數據時,數據源減少一條數據,同樣觸發KVO并在隨后KVO的回調函數中,將界面更新同步。
總體來說,無論對數據源做任何操作,我們都會在KVO的回調函數中,進行程序界面和數據源的同步工作,代碼如下:
@interface ViewController : UIViewController<UITableViewDataSource,UITableViewDelegate>
{
IBOutlet UITableView *_tbv;
}
//遵循KVC的編碼規范
@property (nonatomic,retain) NSMutableArray *dataSrc;
@property (nonatomic,retain) NSString *titleMsg;
//提供KVC中對于容器鍵屬性(dataSrc)的接口
-(NSUInteger)countOfDataSrc;
-(void)insertObject:(id)object inDataSrcAtIndex:(NSUInteger)index;
-(id)objectInDataSrcAtIndex:(NSUInteger)index;
-(void)removeObjectFromDataSrcAtIndex:(NSUInteger)index;
@end
?上述代碼中一共聲明了兩個屬性變量:dataSrc作為數據源,titleMsg作為標題
由于數據源dataSrc是屬于容器類型的數據,根據KVC協議需要申明并實現數組形式的幾個方法
協議的時間內容中直接使用可變數組提供的功能,對上述四個接口進行實現,代碼如下:
//集合屬性的個數
-(NSUInteger)countOfDataSrc
{
return [self.dataSrc count];
}
//集合屬性的新增動作
-(void)insertObject:(id)object inDataSrcAtIndex:(NSUInteger)index
{
[self.dataSrc insertObject:object atIndex:index];
}
//集合屬性的取值動作
-(id)objectInDataSrcAtIndex:(NSUInteger)index
{
return [self.dataSrc objectAtIndex:index];
}
//集合屬性的刪除動作
-(void)removeObjectFromDataSrcAtIndex:(NSUInteger)index
{
[self.dataSrc removeObjectAtIndex:index];
}
?至此KVC的準備工作都做完了,繼續實現KVO機制,對于界面的初始化進口位置,作如下初始化的設置
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//初始化表視圖(UITableView)的數據
self.dataSrc = [[NSMutableArray alloc]initWithCapacity:0];
self.titleMsg = @"沒有動作";
_tbv=[[UITableView alloc]init];
//
//對表視圖的數據進行監視
//
//誰來監視,KVO的監視回調函數就調用誰
[self addObserver:self
//監視的鍵的路徑,我們這里的屬性由于只有一層,所以直接寫dataSrc
forKeyPath:@"dataSrc"
//需要知道表數據改動時的新舊數據,方便我們研究,如果不需要,可以置為0
options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld
//KVO 觸發時,我們收到的額外信息,如果不需要可以置為nil
context:@"testContent"];
[self addObserver:self forKeyPath:@"titleMsg" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"testContent"];
//右邊的按鈕,我們放增加
UIBarButtonItem *addButton=[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(add)];
self.navigationItem.rightBarButtonItem=addButton;
//左邊的按鈕,我們放編輯,主要提供刪除功能
//初始化沒有數據,所以我們disable掉“編輯”按鈕
self.navigationItem.leftBarButtonItem=self.editButtonItem;
self.navigationItem.leftBarButtonItem.enabled=NO;
self.editButtonItem.title=@"編輯";
//標題
self.navigationItem.title=self.titleMsg;
_tbv.delegate=self;
[self.view addSubview:_tbv];
}
?然后寫上必須釋放的方法
-(void)dealloc
{
[self removeObserver:self forKeyPath:@"dataSrc"];
[self removeObserver:self forKeyPath:@"titleMsg"];
}
?隨后當用戶點擊“+”按鈕時,新增的處理函數如下:
//
導航欄上增加按鈕的調用方法
-(
void
)add
{
//
我們打算設置一個靜態的整形記錄當前的排序值
static
int
myIndex=
0
;
//
每次進來,我們就把當前的排序值作為新增的對象
//
所以調用KVO提供的新增接口,插入新元素的位置始終位于最后
[self insertObject:[NSString stringWithFormat:
@"
%d
"
,myIndex] inDataSrcAtIndex:[self countOfDataSrc]];
myIndex
++
;
self.titleMsg
=[NSString stringWithFormat:
@"
新增:%d
"
,myIndex];
}
當用戶點擊“編輯”按鈕時,被調用的系統默認的方法進行重寫
//
當用戶單擊“編輯”按鈕時,對被調用的系統默認方法進行重寫
-(
void
)setEditing:(BOOL)editing animated:(BOOL)animated
{
//
UIViewController 提供的editButtonItem 默認會調用此方法
//
所以我們重寫此方法,第一步就是讓表視圖變成編輯狀態,供我們刪除內容用
[_tbv setEditing:editing animated:animated];
//
第二步讓super繼續操作
//
目的是不改變UIViewController對于editButtonItem原有的動作
//
如果不加,那就是等于我們將這個方法截獲了
//
效果不同體現在:editButtonItem不會在Edit狀態和Done狀態之間切換
[super setEditing:editing animated:animated];
if
(editing)
{
self.editButtonItem.title
=
@"
完成
"
;
}
else
{
self.editButtonItem.title
=
@"
編輯
"
;
}
}
當用戶按下“Delete”后,作為表視圖的代理,“tableView:commitEditingStyle:forRowAtIndexPath:”,這個代理方法將會被調用,所以需要實現如下代碼:
-(
void
)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *
)indexPath
{
if
(editingStyle==
UITableViewCellEditingStyleDelete)
{
self.titleMsg
=[NSString stringWithFormat:
@"
刪除:[%d]
"
,indexPath.row];
[self removeObjectFromDataSrcAtIndex:indexPath.row];
}
}
KVO所觸發的回調函數的實現方式
//
KVO監視某個屬性時,當屬性發生變化會受到此回調
-(
void
)observeValueForKeyPath:(NSString *)keyPath ofObject:(
id
)
object
change:(NSDictionary *)change context:(
void
*
)context
{
if
([keyPath isEqualToString:
@"
titleMsg
"
])
{
[self handleTitleChangeofObject:
object
change:change
context:context];
return
;
}
NSInteger changeRow
=
0
;
//
NSKeyValueChangeIndexesKey鍵中記錄了集合屬性改變位置等重要信息
NSIndexSet *indices=
[change objectForKey:NSKeyValueChangeIndexesKey];
if
(indices)
{
//
我們每次只改集合中的一處地方,所以我們可以用firstIndex來簡單的取出改變的地方
//
如果時多處地方遭到修改,需要使用NSindexSet類提供的getIndexes方法
changeRow=
indices.firstIndex;
}
//
制作NSIndexPath,為了提供給表視圖進行UI更新
NSIndexPath *changeIndexPath=[NSIndexPath indexPathForRow:changeRow inSection:
0
];
//
NSKeyValueChangeKindKey信息中記錄了監視屬性的值變化類型
NSNumber *kind=
[change objectForKey:NSKeyValueChangeKindKey];
switch
([kind intValue]) {
case
NSKeyValueChangeInsertion:
//
此新增方法后,表視圖重繪
[_tbv insertRowsAtIndexPaths:[NSArray arrayWithObjects:changeIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break
;
case
NSKeyValueChangeRemoval:
//
次刪除方法后,表視圖會重繪
[_tbv deleteRowsAtIndexPaths:[NSArray arrayWithObjects:changeIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break
;
default
:
break
;
}
//
控制編輯按鈕
//
如果表數據有記錄
if
([self countOfDataSrc]>
0
)
{
//
讓編輯按鈕可用
self.navigationItem.leftBarButtonItem.enabled=
YES;
}
else
{
//
讓編輯按鈕不可用,并且遵循UIVievController對于不可用時的UI處理(比如變成edit)
[self setEditing:NO animated:YES];
self.navigationItem.leftBarButtonItem.enabled
=
NO;
}
}
下列代碼則是對于界面標題的更新代碼
-(
void
)handleTitleChangeofObject:(
id
)
object
change:(NSDictionary
*
)change
context:(
void
*
)context
{
self.navigationItem.title
=
self.titleMsg;
}
剩下的表視圖的實現方法就不貼了
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

