對(duì)于iOS 開發(fā)者來說,KVO(key-value-observing)的使用大家已經(jīng)不再陌生,而且使用起來也是非常方便。
KVO的簡單使用:
KVOObject *object = [[KVOObject alloc] init];
[object addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
object.name = @"123";
這樣我們就已經(jīng)為object的name屬性添加了監(jiān)聽,只要object的name屬性發(fā)生改變,我們就可以通過KVO的回調(diào)方法獲取其新值。
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"新值為 %@", [change objectForKey:NSKeyValueChangeNewKey]);
}
2017-12-18 16:44:36.096356+0800 KVO[5973:274888] 新值為 123
KVO的基本使用就是這樣,那么KVO是用什么原理實(shí)現(xiàn)的呢?如何自己實(shí)現(xiàn)一個(gè)帶block的KVO呢?
KVO原理:例如在為Object類添加監(jiān)聽時(shí),蘋果動(dòng)態(tài)的為我們添加了一個(gè)類,類的名字是NSKVONotifying_Object,并且NSKVONotifying_Object是Object的子類,然后把指向Object的類指向了NSKVONotifying_Object,然后在子類中重寫setter方法。
直接上代碼:
-(void)sp_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context callBack:(void (^)(id _Nullable))block{
NSString *className = [@"SP_" stringByAppendingString:NSStringFromClass([self class])];
Class newClass = objc_allocateClassPair([self class], className.UTF8String, 0);//動(dòng)態(tài)生成一個(gè)類,類名在原類基礎(chǔ)上加一個(gè)前綴SP_
objc_registerClassPair(newClass);//注冊(cè)該類
object_setClass(self, newClass);//把指針指向子類
class_addMethod(newClass, @selector(setName:), (IMP)classSetName, "v@:@");//重寫set方法
objc_setAssociatedObject(self, &blockKey, block, OBJC_ASSOCIATION_RETAIN_NONATOMIC);//關(guān)聯(lián)block對(duì)象
}
void classSetName(id self,SEL _cmd, NSString * newName){
struct objc_super superClass = {
.receiver = self,
.super_class = class_getSuperclass(object_getClass(self))
};
// 調(diào)用父類中setter方法
objc_msgSendSuper(&superClass,_cmd,newName);
void(^block)(id paramter) = objc_getAssociatedObject(self, &blockKey);
if (block) {
block(newName);
}
}
viewController中調(diào)用:
@interface ViewController ()
@property (nonatomic, strong) KVOObject * object;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
KVOObject *object = [KVOObject new];
[object sp_addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil callBack:^(id _Nullable paramter) {
NSLog(@"block回調(diào): %@\nobject name屬性值: %@",paramter,object.name);
}];
_object = object;
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
static int a = 0;
a++;
_object.name = [NSString stringWithFormat:@"%d",a];
}
KVO[6404:296022] block回調(diào): 1
object name屬性值: 1
2017-12-18 17:05:34.432499+0800 KVO[6404:296022] block回調(diào): 2
object name屬性值: 2