-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathSSYLazyNotificationCenter.m
154 lines (134 loc) · 4.88 KB
/
SSYLazyNotificationCenter.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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#import "SSYLazyNotificationCenter.h"
#import "NSDictionary+KeyPaths.h"
// This is a singleton, but not a "true singletons", because
// I didn't bother to override
// +allocWithZone:
// -copyWithZone:
// -retain
// -retainCount
// -release
// -autorelease
static SSYLazyNotificationCenter* defaultCenter = nil ;
NSString* const constSSYLazyNotificationCenterObserver = @"obsrvr" ;
NSString* const constSSYLazyNotificationSelectorName = @"selName" ;
@implementation SSYLazyNotificationCenter
+ (SSYLazyNotificationCenter*)defaultCenter {
@synchronized(self) {
if (!defaultCenter) {
defaultCenter = [[self alloc] init] ;
}
}
// No autorelease. This sticks around forever.
return defaultCenter ;
}
/*
observations is dictionary of arrays of dictionaries, as shown here:
observations
* noteName1 (array)
* observation11 (dictionary) :(
* observer=observer11,
* selectorName=selectorName11
* )
* observation12 (dictionary) :(
* observer=observer12,
* selectorName=selectorName12
* )
* noteName2 (array)
* observation21 (dictionary) :(
* observer=observer21,
* selectorName=selectorName21
* )
* observation22 (dictionary) :(
* observer=observer22,
* selectorName=selectorName22
* )
*/
- (NSMutableDictionary*)observations {
if (!m_observations) {
m_observations = [[NSMutableDictionary alloc] init] ;
}
return m_observations ;
}
- (NSMutableDictionary*)fireTimers {
if (!m_fireTimers) {
m_fireTimers = [[NSMutableDictionary alloc] init] ;
}
return m_fireTimers ;
}
- (void)dealloc {
[m_observations release] ;
[m_fireTimers release] ;
[super dealloc] ;
}
- (void)addObserver:(id)observer
selector:(SEL)selector
name:(NSString*)name {
NSDictionary* observation = [NSDictionary dictionaryWithObjectsAndKeys:
observer, constSSYLazyNotificationCenterObserver,
NSStringFromSelector(selector), constSSYLazyNotificationSelectorName,
nil] ;
[[self observations] addUniqueObject:observation
toArrayAtKey:name] ;
}
- (void)enqueueNotification:(NSNotification*)note
delay:(NSTimeInterval)delay {
@synchronized (self) {
NSTimer* existingTimer = [[self fireTimers] objectForKey:[note name]] ;
if (existingTimer) {
// Modify the new timer to have the same fire date as the existing timer.
delay = [[existingTimer fireDate] timeIntervalSinceNow] ;
delay = MAX(delay, CGFLOAT_MIN) ;
[existingTimer invalidate] ;
}
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:delay
target:self
selector:@selector(fire:)
userInfo:note
repeats:NO] ;
// Note that the following will replace the existing timer, if any.
[[self fireTimers] setObject:timer
forKey:[note name]] ;
}
}
- (void)fire:(NSTimer*)timer {
@synchronized(self) {
/* Documentation of -[NSTimer userInfo] states:
Do not access this property after the timer is invalidated.
Indeed, in macOS 10.13.3 Beta 2-3, it causes a crash. So, we check
it before doing anything. */
if (timer.isValid) {
NSNotification* note = [timer userInfo] ;
NSString* noteName = [note name] ;
[[self fireTimers] removeObjectForKey:noteName] ;
NSArray* observations = [[self observations] objectForKey:noteName] ;
for (NSDictionary* observation in observations) {
id observer = [observation objectForKey:constSSYLazyNotificationCenterObserver] ;
SEL selector = NSSelectorFromString([observation objectForKey:constSSYLazyNotificationSelectorName]) ;
[observer performSelector:selector
withObject:note] ;
}
}
}
}
- (void)removeObserver:(id)observer {
NSMutableDictionary* newObservations = [[NSMutableDictionary alloc] initWithCapacity:[[self observations] count]] ;
for (NSString* name in [self observations]) {
NSArray* array = [[self observations] objectForKey:name] ;
NSMutableArray* workingArray = [array mutableCopy] ;
for (NSDictionary* observation in array) {
id aObserver = [observation objectForKey:constSSYLazyNotificationCenterObserver] ;
if (aObserver == observer) {
[workingArray removeObject:observation] ;
}
}
NSArray* newArray = [workingArray copy] ;
[workingArray release] ;
[newObservations setObject:newArray
forKey:name] ;
[newArray release] ;
}
[[self observations] removeAllObjects] ;
[[self observations] addEntriesFromDictionary:newObservations] ;
[newObservations release] ;
}
@end