forked from ish-app/ish
-
Notifications
You must be signed in to change notification settings - Fork 0
/
NSObject+SaneKVO.m
72 lines (60 loc) · 2.16 KB
/
NSObject+SaneKVO.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//
// NSObject+SaneKVO.m
// iSH
//
// Created by Theodore Dubois on 11/10/20.
//
#import <objc/runtime.h>
#import "NSObject+SaneKVO.h"
static void *kKVOObservations = &kKVOObservations;
@interface KVOObservation ()
- (instancetype)initWithKeyPath:(NSString *)keyPath object:(id)object block:(void (^)(void))block;
@end
@implementation NSObject (SaneKVO)
- (KVOObservation *)observe:(NSString *)keyPath options:(NSKeyValueObservingOptions)options usingBlock:(void (^)(void))block {
KVOObservation *observation = [[KVOObservation alloc] initWithKeyPath:keyPath object:self block:block];
[self addObserver:observation forKeyPath:keyPath options:options context:NULL];
return observation;
}
- (void)observe:(NSArray<NSString *> *)keyPaths options:(NSKeyValueObservingOptions)options owner:(id)owner usingBlock:(void (^)(id self))block {
__weak id weakOwner = owner;
void (^newBlock)(void) = ^{
id owner = weakOwner;
NSAssert(owner, @"kvo notification shouldn't come to dead object");
block(owner);
};
@synchronized (owner) {
for (NSString *keyPath in keyPaths) {
NSMutableSet *observations = objc_getAssociatedObject(owner, kKVOObservations);
if (observations == nil) {
observations = [NSMutableSet new];
objc_setAssociatedObject(owner, kKVOObservations, observations, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
[observations addObject:[self observe:keyPath options:options usingBlock:newBlock]];
}
}
}
@end
@implementation KVOObservation
- (instancetype)initWithKeyPath:(NSString *)keyPath object:(id)object block:(void (^)(void))block {
if (self = [super init]) {
_keyPath = keyPath;
_object = object;
_block = block;
_enabled = YES;
}
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
_block();
}
- (void)disable {
if (_enabled) {
[_object removeObserver:self forKeyPath:_keyPath context:NULL];
_enabled = NO;
}
}
- (void)dealloc {
[self disable];
}
@end