forked from mikeash/MAKVONotificationCenter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMAKVONotificationCenter.m
161 lines (130 loc) · 4.24 KB
/
MAKVONotificationCenter.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
155
156
157
158
159
160
161
//
// MAKVONotificationCenter.m
// MAKVONotificationCenter
//
// Created by Michael Ash on 10/15/08.
//
#import "MAKVONotificationCenter.h"
#import <libkern/OSAtomic.h>
#import <objc/message.h>
@interface _MAKVONotificationHelper : NSObject
{
id _observer;
SEL _selector;
id _userInfo;
id _target;
NSString* _keyPath;
}
- (id)initWithObserver:(id)observer object:(id)target keyPath:(NSString *)keyPath selector:(SEL)selector userInfo: (id)userInfo options: (NSKeyValueObservingOptions)options;
- (void)deregister;
@end
@implementation _MAKVONotificationHelper
static char MAKVONotificationHelperMagicContext;
- (id)initWithObserver:(id)observer object:(id)target keyPath:(NSString *)keyPath selector:(SEL)selector userInfo: (id)userInfo options: (NSKeyValueObservingOptions)options
{
if((self = [self init]))
{
_observer = observer;
_selector = selector;
_userInfo = [userInfo retain];
_target = target;
_keyPath = [keyPath retain];
[target addObserver:self
forKeyPath:keyPath
options:options
context:&MAKVONotificationHelperMagicContext];
}
return self;
}
- (void)dealloc
{
[_userInfo release];
[_keyPath release];
[super dealloc];
}
#pragma mark -
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if(context == &MAKVONotificationHelperMagicContext)
{
// we only ever sign up for one notification per object, so if we got here
// then we *know* that the key path and object are what we want
((void (*)(id, SEL, NSString *, id, NSDictionary *, id))objc_msgSend)(_observer, _selector, keyPath, object, change, _userInfo);
}
else
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)deregister
{
[_target removeObserver:self forKeyPath:_keyPath];
}
@end
@implementation MAKVONotificationCenter
+ (id)defaultCenter
{
static MAKVONotificationCenter *center = nil;
if(!center)
{
// do a bit of clever atomic setting to make this thread safe
// if two threads try to set simultaneously, one will fail
// and the other will set things up so that the failing thread
// gets the shared center
MAKVONotificationCenter *newCenter = [[self alloc] init];
if(!OSAtomicCompareAndSwapPtrBarrier(nil, newCenter, (void *)¢er))
[newCenter release];
}
return center;
}
- (id)init
{
if((self = [super init]))
{
_observerHelpers = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)dealloc
{
[_observerHelpers release];
[super dealloc];
}
#pragma mark -
- (id)_dictionaryKeyForObserver:(id)observer object:(id)target keyPath:(NSString *)keyPath selector:(SEL)selector
{
return [NSString stringWithFormat:@"%p:%p:%@:%p", observer, target, keyPath, selector];
}
- (void)addObserver:(id)observer object:(id)target keyPath:(NSString *)keyPath selector:(SEL)selector userInfo: (id)userInfo options: (NSKeyValueObservingOptions)options
{
_MAKVONotificationHelper *helper = [[_MAKVONotificationHelper alloc] initWithObserver:observer object:target keyPath:keyPath selector:selector userInfo:userInfo options:options];
id key = [self _dictionaryKeyForObserver:observer object:target keyPath:keyPath selector:selector];
@synchronized(self)
{
[_observerHelpers setObject:helper forKey:key];
}
[helper release];
}
- (void)removeObserver:(id)observer object:(id)target keyPath:(NSString *)keyPath selector:(SEL)selector
{
id key = [self _dictionaryKeyForObserver:observer object:target keyPath:keyPath selector:selector];
_MAKVONotificationHelper *helper = nil;
@synchronized(self)
{
helper = [[_observerHelpers objectForKey:key] retain];
[_observerHelpers removeObjectForKey:key];
}
[helper deregister];
[helper release];
}
@end
@implementation NSObject (MAKVONotification)
- (void)addObserver:(id)observer forKeyPath:(NSString *)keyPath selector:(SEL)selector userInfo:(id)userInfo options:(NSKeyValueObservingOptions)options
{
[[MAKVONotificationCenter defaultCenter] addObserver:observer object:self keyPath:keyPath selector:selector userInfo:userInfo options:options];
}
- (void)removeObserver:(id)observer keyPath:(NSString *)keyPath selector:(SEL)selector
{
[[MAKVONotificationCenter defaultCenter] removeObserver:observer object:self keyPath:keyPath selector:selector];
}
@end