1010#import " IDEFileBreakpoint.h"
1111#import < objc/runtime.h>
1212
13- static void *SCXcodeMinimapBreakpointObserverContext = &SCXcodeMinimapBreakpointObserverContext;
14-
1513@implementation IDEBreakpointManager (SCXcodeMinimap)
1614
1715static void sc_swizzleInstanceMethod (Class class, SEL originalSelector, SEL swizzledSelector) {
@@ -26,6 +24,7 @@ static void sc_swizzleInstanceMethod(Class class, SEL originalSelector, SEL swiz
2624
2725+ (void )load
2826{
27+ sc_swizzleInstanceMethod (self, @selector (_handleWorkspaceContainerRemoved: ), @selector (sc_handleWorkspaceContainerRemoved: ));
2928 sc_swizzleInstanceMethod (self, @selector (addBreakpoint: ), @selector (sc_addBreakpoint: ));
3029 sc_swizzleInstanceMethod (self, @selector (removeBreakpoint: ), @selector (sc_removeBreakpoint: ));
3130}
@@ -40,14 +39,39 @@ - (void)setDelegate:(id<IDEBreakpointManagerDelegate>)delegate
4039 return objc_getAssociatedObject (self, @selector (delegate ));
4140}
4241
42+ - (void )setKvoController : (SCKVOController *)kvoController
43+ {
44+ objc_setAssociatedObject (self, @selector (kvoController ), kvoController, OBJC_ASSOCIATION_RETAIN_NONATOMIC );
45+ }
46+
47+ - (SCKVOController *)kvoController
48+ {
49+ SCKVOController *kvoController = objc_getAssociatedObject (self, @selector (kvoController ));
50+
51+ if (kvoController == nil ) {
52+ kvoController = [[SCKVOController alloc ] init ];
53+ [self setKvoController: kvoController];
54+
55+ for (IDEBreakpoint *breakpoint in self.breakpoints ) {
56+ if ([breakpoint isKindOfClass: [IDEFileBreakpoint class ]]) {
57+ [self _observeBreakpoint: breakpoint];
58+ }
59+ }
60+ }
61+
62+ return kvoController;
63+ }
64+
65+ - (void )sc_handleWorkspaceContainerRemoved : (id )arg1
66+ {
67+ [self .kvoController unobserveAll ];
68+ }
69+
4370- (void )sc_addBreakpoint : (IDEBreakpoint *)breakpoint
4471{
4572 [self sc_addBreakpoint: breakpoint];
4673
47- if ([breakpoint isKindOfClass: [IDEFileBreakpoint class ]]) {
48- [breakpoint addObserver: self forKeyPath: @" location" options: NSKeyValueObservingOptionNew context: SCXcodeMinimapBreakpointObserverContext];
49- [breakpoint addObserver: self forKeyPath: @" shouldBeEnabled" options: NSKeyValueObservingOptionNew context: SCXcodeMinimapBreakpointObserverContext];
50- }
74+ [self _observeBreakpoint: breakpoint];
5175
5276 if ([self .delegate respondsToSelector: @selector (breakpointManagerDidAddBreakpoint: )]) {
5377 [self .delegate breakpointManagerDidAddBreakpoint: self ];
@@ -56,10 +80,7 @@ - (void)sc_addBreakpoint:(IDEBreakpoint *)breakpoint
5680
5781- (void )sc_removeBreakpoint : (IDEBreakpoint *)breakpoint
5882{
59- if ([breakpoint isKindOfClass: [IDEFileBreakpoint class ]]) {
60- [breakpoint removeObserver: self forKeyPath: @" location" context: SCXcodeMinimapBreakpointObserverContext];
61- [breakpoint removeObserver: self forKeyPath: @" shouldBeEnabled" context: SCXcodeMinimapBreakpointObserverContext];
62- }
83+ [self .kvoController unobserveObject: breakpoint];
6384
6485 [self sc_removeBreakpoint: breakpoint];
6586
@@ -68,13 +89,178 @@ - (void)sc_removeBreakpoint:(IDEBreakpoint *)breakpoint
6889 }
6990}
7091
71- - (void )observeValueForKeyPath : ( NSString *)keyPath ofObject : ( id ) object change : ( NSDictionary *) change context : ( void *) context
92+ - (void )_observeBreakpoint : (IDEBreakpoint *)breakpoint
7293{
73- if (context == SCXcodeMinimapBreakpointObserverContext) {
94+ [self .kvoController observeObject: breakpoint forKeyPath: @" location" options: NSKeyValueObservingOptionNew block: ^(id object, NSDictionary *change) {
95+ if ([self .delegate respondsToSelector: @selector (breakpointManagerDidChangeBreakpoint: )]) {
96+ [self .delegate breakpointManagerDidChangeBreakpoint: self ];
97+ }
98+ }];
99+
100+ [self .kvoController observeObject: breakpoint forKeyPath: @" shouldBeEnabled" options: NSKeyValueObservingOptionNew block: ^(id object, NSDictionary *change) {
74101 if ([self .delegate respondsToSelector: @selector (breakpointManagerDidChangeBreakpoint: )]) {
75102 [self .delegate breakpointManagerDidChangeBreakpoint: self ];
76103 }
104+ }];
105+ }
106+
107+ @end
108+
109+
110+ @interface SCKVOKeyPathInfo : NSObject
111+
112+ @property (nonatomic , strong ) NSMutableSet *blocks;
113+
114+ @end
115+
116+
117+ @interface SCKVOInfo : NSObject
118+
119+ @property (nonatomic , weak ) id observer;
120+ @property (nonatomic , strong ) NSMutableDictionary *keyPathInfos;
121+
122+ - (instancetype )initWithObserver : (id )observer ;
123+
124+ @end
125+
126+
127+ @interface SCKVOController ()
128+
129+ @property (nonatomic , strong ) NSMapTable *mapTable;
130+
131+ @end
132+
133+
134+ @implementation SCKVOController
135+
136+ - (void )dealloc
137+ {
138+ [self unobserveAll ];
139+ }
140+
141+ - (void )observeObject : (id )object forKeyPath : (NSString *)keyPath options : (NSKeyValueObservingOptions )options block : (SCKVONotificationBlock)block
142+ {
143+ NSParameterAssert (object);
144+ NSParameterAssert (keyPath);
145+ NSParameterAssert (block);
146+
147+ SCKVOInfo *info = [self .mapTable objectForKey: object];
148+
149+ if (!info) {
150+ info = [[SCKVOInfo alloc ] initWithObserver: object];
151+ [self .mapTable setObject: info forKey: object];
152+ }
153+
154+ SCKVOKeyPathInfo *kvoKeyPathInfo = [info.keyPathInfos objectForKey: keyPath];
155+
156+ BOOL registerObserver = NO ;
157+
158+ if (!kvoKeyPathInfo) {
159+ kvoKeyPathInfo = [[SCKVOKeyPathInfo alloc ] init ];
160+ [info.keyPathInfos setValue: kvoKeyPathInfo forKey: keyPath];
161+ registerObserver = YES ;
162+ }
163+
164+ [kvoKeyPathInfo.blocks addObject: [block copy ]];
165+
166+ if (registerObserver) {
167+ [object addObserver: self forKeyPath: keyPath options: options context: NULL ];
168+ }
169+ }
170+
171+ - (void )unobserveObject : (id )object forKeyPath : (NSString *)keyPath
172+ {
173+ NSParameterAssert (object);
174+ NSParameterAssert (keyPath);
175+
176+ SCKVOInfo *info = [self .mapTable objectForKey: object];
177+
178+ if (info) {
179+ if (info.keyPathInfos [keyPath]) {
180+ [info.keyPathInfos removeObjectForKey: keyPath];
181+ [object removeObserver: self forKeyPath: keyPath context: NULL ];
182+
183+ if (info.keyPathInfos .count == 0 ) {
184+ [self .mapTable removeObjectForKey: object];
185+ }
186+ }
187+ }
188+ }
189+
190+ - (void )unobserveObject : (id )object
191+ {
192+ NSParameterAssert (object);
193+
194+ SCKVOInfo *info = [self .mapTable objectForKey: object];
195+ if (info) {
196+ for (NSString *keyPath in [info.keyPathInfos allKeys ]) {
197+ [self unobserveObject: object forKeyPath: keyPath];
198+ }
199+ }
200+ }
201+
202+ - (void )unobserveAll
203+ {
204+ NSMapTable *mapTable = [self .mapTable copy ];
205+
206+ for (id object in mapTable) {
207+ [self unobserveObject: object];
208+ }
209+ }
210+
211+ - (NSMapTable *)mapTable
212+ {
213+ if (!_mapTable) {
214+ _mapTable = [NSMapTable mapTableWithKeyOptions: NSPointerFunctionsWeakMemory |NSPointerFunctionsObjectPointerPersonality valueOptions: NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPersonality];
215+ }
216+
217+ return _mapTable;
218+ }
219+
220+ - (void )observeValueForKeyPath : (NSString *)keyPath
221+ ofObject : (id )object
222+ change : (NSDictionary *)change
223+ context : (void *)context
224+ {
225+ SCKVOInfo *info = [self .mapTable objectForKey: object];
226+
227+ if (info) {
228+ SCKVOKeyPathInfo *kvoKeyPathInfo = [info.keyPathInfos objectForKey: keyPath];
229+
230+ if (kvoKeyPathInfo) {
231+ for (SCKVONotificationBlock block in kvoKeyPathInfo.blocks ) {
232+ block (object, change);
233+ }
234+ }
235+ }
236+ }
237+
238+ @end
239+
240+
241+ @implementation SCKVOKeyPathInfo
242+
243+ - (instancetype )init
244+ {
245+ if (self = [super init ]) {
246+ self.blocks = [NSMutableSet set ];
247+ }
248+
249+ return self;
250+ }
251+
252+ @end
253+
254+
255+ @implementation SCKVOInfo
256+
257+ - (instancetype )initWithObserver : (id )observer
258+ {
259+ if (self = [super init ]) {
260+ self.observer = observer;
261+ self.keyPathInfos = [NSMutableDictionary dictionary ];
77262 }
263+ return self;
78264}
79265
80266@end
0 commit comments