New file |
| | |
| | | // |
| | | // NSObject+YYAddForKVO.m |
| | | // YYCategories <https://github.com/ibireme/YYCategories> |
| | | // |
| | | // 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 "YYCategoriesMacro.h" |
| | | #import <objc/objc.h> |
| | | #import <objc/runtime.h> |
| | | |
| | | 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 |