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

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 <algorithm>
9 #include <vector>
8 10
9 #include "base/logging.h" 11 #include "base/logging.h"
10 #include "base/mac/scoped_nsobject.h" 12 #include "base/mac/scoped_nsobject.h"
11 13
14 @interface CRBProtocolObservers () {
15 base::scoped_nsobject<Protocol> _protocol;
16 // ivars declared here are private to the implementation but must be
17 // public for allowing the C++ |Iterator| class access to those ivars.
18 @public
19 // vector of weak pointers to observers.
20 std::vector<__unsafe_unretained id> _observers;
21 // The nested level of observer iteration.
22 // A depth of 0 means nobody is currently iterating on the list of observers.
23 int _invocationDepth;
24 }
25
26 // Removes nil observers from the list and is called when the
27 // |_invocationDepth| reaches 0.
28 - (void)compact;
29
30 @end
31
32 namespace {
33
34 class Iterator {
35 public:
36 explicit Iterator(CRBProtocolObservers* protocol_observers);
37 ~Iterator();
38 id GetNext();
39
40 private:
41 CRBProtocolObservers* protocol_observers_;
42 size_t index_;
43 size_t max_index_;
44 };
45
46 Iterator::Iterator(CRBProtocolObservers* protocol_observers)
47 : protocol_observers_(protocol_observers),
48 index_(0),
49 max_index_(protocol_observers->_observers.size()) {
50 DCHECK(protocol_observers_);
51 ++protocol_observers->_invocationDepth;
52 }
53
54 Iterator::~Iterator() {
55 if (protocol_observers_ && --protocol_observers_->_invocationDepth == 0)
56 [protocol_observers_ compact];
57 }
58
59 id Iterator::GetNext() {
60 if (!protocol_observers_)
61 return nil;
62 auto& observers = protocol_observers_->_observers;
63 // Skip nil elements.
64 size_t max_index = std::min(max_index_, observers.size());
65 while (index_ < max_index && !observers[index_])
66 ++index_;
67 return index_ < max_index ? observers[index_++] : nil;
68 }
69 }
70
12 @interface CRBProtocolObservers () 71 @interface CRBProtocolObservers ()
13 72
14 // Designated initializer. 73 // Designated initializer.
15 - (id)initWithProtocol:(Protocol*)protocol; 74 - (id)initWithProtocol:(Protocol*)protocol;
16 75
17 @end 76 @end
18 77
19 @implementation CRBProtocolObservers { 78 @implementation CRBProtocolObservers
20 base::scoped_nsobject<Protocol> _protocol;
21 base::scoped_nsobject<NSHashTable> _observers;
22 }
23 79
24 + (CRBProtocolObservers*)observersWithProtocol:(Protocol*)protocol { 80 + (instancetype)observersWithProtocol:(Protocol*)protocol {
25 return [[[self alloc] initWithProtocol:protocol] autorelease]; 81 return [[[self alloc] initWithProtocol:protocol] autorelease];
26 } 82 }
27 83
28 - (id)init { 84 - (id)init {
29 NOTREACHED(); 85 NOTREACHED();
30 return nil; 86 return nil;
31 } 87 }
32 88
33 - (id)initWithProtocol:(Protocol*)protocol { 89 - (id)initWithProtocol:(Protocol*)protocol {
34 self = [super init]; 90 self = [super init];
35 if (self) { 91 if (self) {
36 _protocol.reset([protocol retain]); 92 _protocol.reset([protocol retain]);
37 _observers.reset([[NSHashTable weakObjectsHashTable] retain]);
38 } 93 }
39 return self; 94 return self;
40 } 95 }
41 96
42 - (Protocol*)protocol { 97 - (Protocol*)protocol {
43 return _protocol.get(); 98 return _protocol.get();
44 } 99 }
45 100
46 - (void)addObserver:(id)observer { 101 - (void)addObserver:(id)observer {
102 DCHECK(observer);
47 DCHECK([observer conformsToProtocol:self.protocol]); 103 DCHECK([observer conformsToProtocol:self.protocol]);
48 [_observers addObject:observer]; 104
105 if (std::find(_observers.begin(), _observers.end(), observer) !=
106 _observers.end())
107 return;
108
109 _observers.push_back(observer);
49 } 110 }
50 111
51 - (void)removeObserver:(id)observer { 112 - (void)removeObserver:(id)observer {
52 [_observers removeObject:observer]; 113 DCHECK(observer);
114 auto it = std::find(_observers.begin(), _observers.end(), observer);
115 if (it != _observers.end()) {
116 if (_invocationDepth)
117 *it = nil;
118 else
119 _observers.erase(it);
120 }
121 }
122
123 - (BOOL)empty {
124 int count = 0;
125 for (id observer : _observers) {
126 if (observer != nil)
127 ++count;
128 }
129 return count == 0;
53 } 130 }
54 131
55 #pragma mark - NSObject 132 #pragma mark - NSObject
56 133
57 - (NSMethodSignature*)methodSignatureForSelector:(SEL)selector { 134 - (NSMethodSignature*)methodSignatureForSelector:(SEL)selector {
58 NSMethodSignature* signature = [super methodSignatureForSelector:selector]; 135 NSMethodSignature* signature = [super methodSignatureForSelector:selector];
59 if (signature) 136 if (signature)
60 return signature; 137 return signature;
61 138
62 // Look for a required method in the protocol. protocol_getMethodDescription 139 // Look for a required method in the protocol. protocol_getMethodDescription
(...skipping 10 matching lines...) Expand all
73 return [NSMethodSignature signatureWithObjCTypes:description.types]; 150 return [NSMethodSignature signatureWithObjCTypes:description.types];
74 151
75 // There is neither a required nor optional method with this selector in the 152 // There is neither a required nor optional method with this selector in the
76 // protocol, so invoke -[NSObject doesNotRecognizeSelector:] to raise 153 // protocol, so invoke -[NSObject doesNotRecognizeSelector:] to raise
77 // NSInvalidArgumentException. 154 // NSInvalidArgumentException.
78 [self doesNotRecognizeSelector:selector]; 155 [self doesNotRecognizeSelector:selector];
79 return nil; 156 return nil;
80 } 157 }
81 158
82 - (void)forwardInvocation:(NSInvocation*)invocation { 159 - (void)forwardInvocation:(NSInvocation*)invocation {
160 DCHECK(invocation);
161 if (_observers.empty())
162 return;
83 SEL selector = [invocation selector]; 163 SEL selector = [invocation selector];
84 base::scoped_nsobject<NSArray> observers([[_observers allObjects] retain]); 164 Iterator it(self);
85 for (id observer in observers.get()) { 165 id observer;
166 while ((observer = it.GetNext()) != nil) {
86 if ([observer respondsToSelector:selector]) 167 if ([observer respondsToSelector:selector])
87 [invocation invokeWithTarget:observer]; 168 [invocation invokeWithTarget:observer];
88 } 169 }
89 } 170 }
90 171
91 - (void)executeOnObservers:(ExecutionWithObserverBlock)callback { 172 - (void)executeOnObservers:(ExecutionWithObserverBlock)callback {
92 DCHECK(callback); 173 DCHECK(callback);
93 base::scoped_nsobject<NSArray> observers([[_observers allObjects] retain]); 174 if (_observers.empty())
94 for (id observer in observers.get()) { 175 return;
176 Iterator it(self);
177 id observer;
178 while ((observer = it.GetNext()) != nil)
95 callback(observer); 179 callback(observer);
96 } 180 }
181
182 #pragma mark - Private
183
184 - (void)compact {
185 DCHECK(!_invocationDepth);
186 _observers.erase(std::remove(_observers.begin(), _observers.end(), nil),
187 _observers.end());
97 } 188 }
98 189
99 @end 190 @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