? ? 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元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
