Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(39)

Side by Side Diff: base/ios/crb_protocol_observers.mm

Issue 1157863009: CRBProtocolObservers can now be mutated while forwarding methods. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/ios/crb_protocol_observers.h ('k') | base/ios/crb_protocol_observers_unittest.mm » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "base/ios/crb_protocol_observers.h" 5 #import "base/ios/crb_protocol_observers.h"
6 6
7 #include <objc/runtime.h> 7 #include <objc/runtime.h>
8 #include <vector>
8 9
9 #include "base/logging.h" 10 #include "base/logging.h"
10 #include "base/mac/scoped_nsobject.h" 11 #include "base/mac/scoped_nsobject.h"
11 12
13 @interface CRBProtocolObservers () {
14 base::scoped_nsobject<Protocol> _protocol;
15 // ivar declared here are private to the implementation but must be
16 // public for allowing the C++ |Iterator| class access to those ivars.
17 @public
18 // vector of weak pointers to observers.
19 std::vector<__unsafe_unretained id> _observers;
20 // Indicate the nested level of observer iteration.
21 // A depth of 0 means nobody is currently iterating on the list of observers.
22 int _invocation_depth;
droger 2015/06/04 12:29:53 _invocationDepth
jbbegue 2015/06/04 12:41:45 Done.
23 }
24
25 // The methods will remove nil observers from the list and will be called when
droger 2015/06/04 12:29:53 s/will remove/removes/ s/will be/is/
jbbegue 2015/06/04 12:41:46 Done.
26 // the |_invocation_depth| reaches 0.
27 - (void)compact;
28
29 @end
30
31 namespace {
32
33 class Iterator {
34 public:
35 explicit Iterator(CRBProtocolObservers* protocol_observers);
36 ~Iterator();
37 id GetNext();
38
39 private:
40 CRBProtocolObservers* protocol_observers_;
41 size_t index_;
42 size_t max_index_;
43 };
44
45 Iterator::Iterator(CRBProtocolObservers* protocol_observers)
46 : protocol_observers_(protocol_observers),
47 index_(0),
48 max_index_(protocol_observers->_observers.size()) {
49 DCHECK(protocol_observers_);
50 ++protocol_observers->_invocation_depth;
51 }
52
53 Iterator::~Iterator() {
54 if (protocol_observers_ && --protocol_observers_->_invocation_depth == 0)
55 [protocol_observers_ compact];
56 }
57
58 id Iterator::GetNext() {
59 if (!protocol_observers_)
60 return nil;
61 auto& observers = protocol_observers_->_observers;
62 // Skip null elements.
63 size_t max_index = std::min(max_index_, observers.size());
64 while (index_ < max_index && !observers[index_])
65 ++index_;
66 return index_ < max_index ? observers[index_++] : nil;
67 }
68 }
69
12 @interface CRBProtocolObservers () 70 @interface CRBProtocolObservers ()
13 71
14 // Designated initializer. 72 // Designated initializer.
15 - (id)initWithProtocol:(Protocol*)protocol; 73 - (id)initWithProtocol:(Protocol*)protocol;
16 74
17 @end 75 @end
18 76
19 @implementation CRBProtocolObservers { 77 @implementation CRBProtocolObservers
20 base::scoped_nsobject<Protocol> _protocol;
21 base::scoped_nsobject<NSHashTable> _observers;
22 }
23 78
24 + (CRBProtocolObservers*)observersWithProtocol:(Protocol*)protocol { 79 + (instancetype)observersWithProtocol:(Protocol*)protocol {
25 return [[[self alloc] initWithProtocol:protocol] autorelease]; 80 return [[[self alloc] initWithProtocol:protocol] autorelease];
26 } 81 }
27 82
28 - (id)init { 83 - (id)init {
29 NOTREACHED(); 84 NOTREACHED();
30 return nil; 85 return nil;
31 } 86 }
32 87
33 - (id)initWithProtocol:(Protocol*)protocol { 88 - (id)initWithProtocol:(Protocol*)protocol {
34 self = [super init]; 89 self = [super init];
35 if (self) { 90 if (self) {
36 _protocol.reset([protocol retain]); 91 _protocol.reset([protocol retain]);
37 _observers.reset([[NSHashTable weakObjectsHashTable] retain]);
38 } 92 }
39 return self; 93 return self;
40 } 94 }
41 95
42 - (Protocol*)protocol { 96 - (Protocol*)protocol {
43 return _protocol.get(); 97 return _protocol.get();
44 } 98 }
45 99
46 - (void)addObserver:(id)observer { 100 - (void)addObserver:(id)observer {
101 DCHECK(observer);
47 DCHECK([observer conformsToProtocol:self.protocol]); 102 DCHECK([observer conformsToProtocol:self.protocol]);
48 [_observers addObject:observer]; 103
104 if (std::find(_observers.begin(), _observers.end(), observer) !=
105 _observers.end())
106 return;
107
108 _observers.push_back(observer);
49 } 109 }
50 110
51 - (void)removeObserver:(id)observer { 111 - (void)removeObserver:(id)observer {
52 [_observers removeObject:observer]; 112 DCHECK(observer);
113 auto it = std::find(_observers.begin(), _observers.end(), observer);
114 if (it != _observers.end()) {
115 if (_invocation_depth)
116 *it = nil;
117 else
118 _observers.erase(it);
119 }
120 }
121
122 - (BOOL)empty {
123 return _observers.empty();
53 } 124 }
54 125
55 #pragma mark - NSObject 126 #pragma mark - NSObject
56 127
57 - (NSMethodSignature*)methodSignatureForSelector:(SEL)selector { 128 - (NSMethodSignature*)methodSignatureForSelector:(SEL)selector {
58 NSMethodSignature* signature = [super methodSignatureForSelector:selector]; 129 NSMethodSignature* signature = [super methodSignatureForSelector:selector];
59 if (signature) 130 if (signature)
60 return signature; 131 return signature;
61 132
62 // Look for a required method in the protocol. protocol_getMethodDescription 133 // Look for a required method in the protocol. protocol_getMethodDescription
(...skipping 10 matching lines...) Expand all
73 return [NSMethodSignature signatureWithObjCTypes:description.types]; 144 return [NSMethodSignature signatureWithObjCTypes:description.types];
74 145
75 // There is neither a required nor optional method with this selector in the 146 // There is neither a required nor optional method with this selector in the
76 // protocol, so invoke -[NSObject doesNotRecognizeSelector:] to raise 147 // protocol, so invoke -[NSObject doesNotRecognizeSelector:] to raise
77 // NSInvalidArgumentException. 148 // NSInvalidArgumentException.
78 [self doesNotRecognizeSelector:selector]; 149 [self doesNotRecognizeSelector:selector];
79 return nil; 150 return nil;
80 } 151 }
81 152
82 - (void)forwardInvocation:(NSInvocation*)invocation { 153 - (void)forwardInvocation:(NSInvocation*)invocation {
154 DCHECK(invocation);
155 if (_observers.empty())
156 return;
83 SEL selector = [invocation selector]; 157 SEL selector = [invocation selector];
84 base::scoped_nsobject<NSArray> observers([[_observers allObjects] retain]); 158 Iterator it(self);
85 for (id observer in observers.get()) { 159 id observer;
160 while ((observer = it.GetNext()) != nil) {
86 if ([observer respondsToSelector:selector]) 161 if ([observer respondsToSelector:selector])
87 [invocation invokeWithTarget:observer]; 162 [invocation invokeWithTarget:observer];
88 } 163 }
89 } 164 }
90 165
91 - (void)executeOnObservers:(ExecutionWithObserverBlock)callback { 166 - (void)executeOnObservers:(ExecutionWithObserverBlock)callback {
92 DCHECK(callback); 167 DCHECK(callback);
93 base::scoped_nsobject<NSArray> observers([[_observers allObjects] retain]); 168 if (_observers.empty())
94 for (id observer in observers.get()) { 169 return;
170 Iterator it(self);
171 id observer;
172 while ((observer = it.GetNext()) != nil)
95 callback(observer); 173 callback(observer);
96 } 174 }
175
176 #pragma mark - Private
177
178 - (void)compact {
179 _observers.erase(std::remove(_observers.begin(), _observers.end(), nil),
sdefresne 2015/06/04 12:32:48 nit: DCHECK(!_invocation_depth);
jbbegue 2015/06/04 12:41:46 Done.
180 _observers.end());
97 } 181 }
98 182
99 @end 183 @end
OLDNEW
« no previous file with comments | « base/ios/crb_protocol_observers.h ('k') | base/ios/crb_protocol_observers_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698