// // NSObject+YYAddForKVO.m // YYKit // // Created by ibireme on 14/10/15. // Copyright (c) 2015 ibireme. // // This source code is licensed under the MIT-style license found in the // LICENSE file in the root directory of this source tree. // #import "NSObject+YYAddForKVO.h" #import "YYKitMacro.h" #import #import YYSYNTH_DUMMY_CLASS(NSObject_YYAddForKVO) static const int block_key; @interface _YYNSObjectKVOBlockTarget : NSObject @property (nonatomic, copy) void (^block)(__weak id obj, id oldVal, id newVal); - (id)initWithBlock:(void (^)(__weak id obj, id oldVal, id newVal))block; @end @implementation _YYNSObjectKVOBlockTarget - (id)initWithBlock:(void (^)(__weak id obj, id oldVal, id newVal))block { self = [super init]; if (self) { self.block = block; } return self; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (!self.block) return; BOOL isPrior = [[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue]; if (isPrior) return; NSKeyValueChange changeKind = [[change objectForKey:NSKeyValueChangeKindKey] integerValue]; if (changeKind != NSKeyValueChangeSetting) return; id oldVal = [change objectForKey:NSKeyValueChangeOldKey]; if (oldVal == [NSNull null]) oldVal = nil; id newVal = [change objectForKey:NSKeyValueChangeNewKey]; if (newVal == [NSNull null]) newVal = nil; self.block(object, oldVal, newVal); } @end @implementation NSObject (YYAddForKVO) - (void)addObserverBlockForKeyPath:(NSString *)keyPath block:(void (^)(__weak id obj, id oldVal, id newVal))block { if (!keyPath || !block) return; _YYNSObjectKVOBlockTarget *target = [[_YYNSObjectKVOBlockTarget alloc] initWithBlock:block]; NSMutableDictionary *dic = [self _yy_allNSObjectObserverBlocks]; NSMutableArray *arr = dic[keyPath]; if (!arr) { arr = [NSMutableArray new]; dic[keyPath] = arr; } [arr addObject:target]; [self addObserver:target forKeyPath:keyPath options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL]; } - (void)removeObserverBlocksForKeyPath:(NSString *)keyPath { if (!keyPath) return; NSMutableDictionary *dic = [self _yy_allNSObjectObserverBlocks]; NSMutableArray *arr = dic[keyPath]; [arr enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) { [self removeObserver:obj forKeyPath:keyPath]; }]; [dic removeObjectForKey:keyPath]; } - (void)removeObserverBlocks { NSMutableDictionary *dic = [self _yy_allNSObjectObserverBlocks]; [dic enumerateKeysAndObjectsUsingBlock: ^(NSString *key, NSArray *arr, BOOL *stop) { [arr enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) { [self removeObserver:obj forKeyPath:key]; }]; }]; [dic removeAllObjects]; } - (NSMutableDictionary *)_yy_allNSObjectObserverBlocks { NSMutableDictionary *targets = objc_getAssociatedObject(self, &block_key); if (!targets) { targets = [NSMutableDictionary new]; objc_setAssociatedObject(self, &block_key, targets, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } return targets; } @end