在IOS中都有一種雙向綁定機制,如果資料模型修改了之後會立即反映到UI視圖上,它叫做Key Value Observing(簡稱KVO)。KVO其實是一種觀察者模式,利用它可以很容易實作視圖元件和資料模型的分離,當資料模型的屬性值改變之後作為監聽器的視圖元件就會被激發,激發時就會回調監聽器自身。
在ObjC中要實作KVO則必須實作NSKeyValueObServing協定,不過幸運的是NSObject已經實作了該協定,是以幾乎所有的ObjC對象都可以使用KVO。
Object-C
在ObjC中使用KVO操作常用的方法如下:
1. 注冊指定Key路徑的監聽器: addObserver: forKeyPath: options: context:
2. 删除指定Key路徑的監聽器: removeObserver: forKeyPath、removeObserver: forKeyPath: context:
3. 回調監聽: observeValueForKeyPath: ofObject: change: context:
KVO的使用步驟也比較簡單:
1.通過addObserver: forKeyPath: options: context:為被監聽對象(它通常是資料模型)注冊監聽器
2.重寫監聽器的observeValueForKeyPath: ofObject: change: context:方法
模型資料
User.h
//
// User.h
// KVO
//
// Created by yangjun on 15/10/10.
// Copyright © 2015年 六月. All rights reserved.
//
#import <Foundation/Foundation.h>
/** 使用者*/
@interface User : NSObject
@property (nonatomic, copy) NSString *userName; ///< 使用者名
@end
XCTestCase測試
//
// KVOTests.m
// KVOTests
//
// Created by yangjun on 15/10/10.
// Copyright © 2015年 六月. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "User.h"
@interface KVOTests : XCTestCase
{
User *_user;
}
@end
@implementation KVOTests
- (void)setUp {
[super setUp];
_user = [[User alloc] init];
[_user addObserver:self forKeyPath:@"userName" options:NSKeyValueObservingOptionNew context:nil];// 添加監聽
}
- (void)tearDown {
[super tearDown];
_user = nil;
}
- (void)testExample {
_user.userName = @"yangjun";
[_user removeObserver:self forKeyPath:@"userName"];// 取消監聽
_user.userName = @"IOS";
}
#pragma mark 監聽
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([@"userName" isEqualToString:keyPath]) {// 這裡隻處理userName屬性
NSLog(@"userName:%@; change:%@", ((User *)object).userName, change);
}
}
@end
輸出
2015-10-10 15:23:47.660 KVO[10344:789286] userName:yangjun; change:{
kind = 1;
new = yangjun;
}
Swift
在Swift中使用KVO操作常用的方法如下:
1. 注冊指定Key路徑的監聽器:
addObserver(observer: NSObject, forKeyPath keyPath: String, options: NSKeyValueObservingOptions, context: UnsafeMutablePointer<Void>)
2. 删除指定Key路徑的監聽器:
removeObserver(observer: NSObject, forKeyPath keyPath: String)
3. 回調監聽:
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>)
KVO的使用步驟也比較簡單:
1.通過addObserver: forKeyPath: options: context:為被監聽對象(它通常是資料模型)注冊監聽器
2.重寫監聽器的observeValueForKeyPath: ofObject: change: context:方法
模型
User.swift
//
// User.swift
// KVO
//
// Created by yangjun on 15/10/10.
// Copyright © 2015年 六月. All rights reserved.
//
import Foundation
/// 使用者
class User: NSObject {
/// 使用者名
dynamic var userName:String?
override init() {
userName = ""
}
}
XCTestCase測試
//
// KVOTests.swift
// KVOTests
//
// Created by yangjun on 15/10/10.
// Copyright © 2015年 六月. All rights reserved.
//
import XCTest
/// KVO測試
class KVOTests: XCTestCase {
/// 使用者
var user:User!;
// MARK: 開始
override func setUp() {
super.setUp()
self.user = User()
self.user.addObserver(self, forKeyPath: "userName", options: NSKeyValueObservingOptions.New, context: nil)// 監聽(KVO的屬性必須設定dynamic)
}
// MARK: 結束
override func tearDown() {
super.tearDown()
self.user.removeObserver(self, forKeyPath: "userName")
self.user.userName = "YangJun"
}
// MARK: 測試用例
func testExample() {
self.user.userName = "陽君"
}
// MARK: - 監聽
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if "userName" == keyPath {
print("userName:\((object as? User)?.userName); change:\(change)")
}
}
}
輸出
userName:Optional("陽君"); change:Optional(["new": 陽君, "kind": ])