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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: base/ios/crb_protocol_observers.mm
diff --git a/base/ios/crb_protocol_observers.mm b/base/ios/crb_protocol_observers.mm
index ee9e23fcb4e255c29ff0a33590027a25360d9d0e..4e61e65b7b640ccd5f9f8980f6547710afd64b6e 100644
--- a/base/ios/crb_protocol_observers.mm
+++ b/base/ios/crb_protocol_observers.mm
@@ -5,10 +5,68 @@
#import "base/ios/crb_protocol_observers.h"
#include <objc/runtime.h>
+#include <vector>
#include "base/logging.h"
#include "base/mac/scoped_nsobject.h"
+@interface CRBProtocolObservers () {
+ base::scoped_nsobject<Protocol> _protocol;
+ // ivar declared here are private to the implementation but must be
stuartmorgan 2015/06/04 13:40:06 ivars
jbbegue 2015/06/04 16:03:11 Done.
+ // public for allowing the C++ |Iterator| class access to those ivars.
+ @public
+ // vector of weak pointers to observers.
+ std::vector<__unsafe_unretained id> _observers;
+ // Indicate the nested level of observer iteration.
stuartmorgan 2015/06/04 13:40:06 s/Indicate the/The/
jbbegue 2015/06/04 16:03:11 Done.
+ // A depth of 0 means nobody is currently iterating on the list of observers.
+ int _invocationDepth;
+}
+
+// The methods removes nil observers from the list and is called when the
stuartmorgan 2015/06/04 13:40:06 s/The method removes/Removes/
jbbegue 2015/06/04 16:03:11 Done.
+// |_invocationDepth| reaches 0.
+- (void)compact;
+
+@end
+
+namespace {
+
+class Iterator {
+ public:
+ explicit Iterator(CRBProtocolObservers* protocol_observers);
+ ~Iterator();
+ id GetNext();
+
+ private:
+ CRBProtocolObservers* protocol_observers_;
+ size_t index_;
+ size_t max_index_;
+};
+
+Iterator::Iterator(CRBProtocolObservers* protocol_observers)
+ : protocol_observers_(protocol_observers),
+ index_(0),
+ max_index_(protocol_observers->_observers.size()) {
+ DCHECK(protocol_observers_);
+ ++protocol_observers->_invocationDepth;
+}
+
+Iterator::~Iterator() {
+ if (protocol_observers_ && --protocol_observers_->_invocationDepth == 0)
+ [protocol_observers_ compact];
+}
+
+id Iterator::GetNext() {
+ if (!protocol_observers_)
+ return nil;
+ auto& observers = protocol_observers_->_observers;
+ // Skip null elements.
stuartmorgan 2015/06/04 13:40:06 nil
jbbegue 2015/06/04 16:03:11 Done.
+ size_t max_index = std::min(max_index_, observers.size());
+ while (index_ < max_index && !observers[index_])
+ ++index_;
+ return index_ < max_index ? observers[index_++] : nil;
+}
+}
+
@interface CRBProtocolObservers ()
// Designated initializer.
@@ -16,12 +74,9 @@
@end
-@implementation CRBProtocolObservers {
- base::scoped_nsobject<Protocol> _protocol;
- base::scoped_nsobject<NSHashTable> _observers;
-}
+@implementation CRBProtocolObservers
-+ (CRBProtocolObservers*)observersWithProtocol:(Protocol*)protocol {
++ (instancetype)observersWithProtocol:(Protocol*)protocol {
return [[[self alloc] initWithProtocol:protocol] autorelease];
}
@@ -34,7 +89,6 @@
self = [super init];
if (self) {
_protocol.reset([protocol retain]);
- _observers.reset([[NSHashTable weakObjectsHashTable] retain]);
}
return self;
}
@@ -44,12 +98,29 @@
}
- (void)addObserver:(id)observer {
+ DCHECK(observer);
DCHECK([observer conformsToProtocol:self.protocol]);
- [_observers addObject:observer];
+
+ if (std::find(_observers.begin(), _observers.end(), observer) !=
+ _observers.end())
+ return;
+
+ _observers.push_back(observer);
}
- (void)removeObserver:(id)observer {
- [_observers removeObject:observer];
+ DCHECK(observer);
+ auto it = std::find(_observers.begin(), _observers.end(), observer);
+ if (it != _observers.end()) {
+ if (_invocationDepth)
+ *it = nil;
+ else
+ _observers.erase(it);
+ }
+}
+
+- (BOOL)empty {
stuartmorgan 2015/06/04 13:40:06 It's probably worth calling out in the header that
jbbegue 2015/06/04 16:03:11 Good point thanks, I prefer to fix the implementat
+ return _observers.empty();
}
#pragma mark - NSObject
@@ -80,9 +151,13 @@
}
- (void)forwardInvocation:(NSInvocation*)invocation {
+ DCHECK(invocation);
+ if (_observers.empty())
+ return;
SEL selector = [invocation selector];
- base::scoped_nsobject<NSArray> observers([[_observers allObjects] retain]);
- for (id observer in observers.get()) {
+ Iterator it(self);
+ id observer;
+ while ((observer = it.GetNext()) != nil) {
if ([observer respondsToSelector:selector])
[invocation invokeWithTarget:observer];
}
@@ -90,10 +165,20 @@
- (void)executeOnObservers:(ExecutionWithObserverBlock)callback {
DCHECK(callback);
- base::scoped_nsobject<NSArray> observers([[_observers allObjects] retain]);
- for (id observer in observers.get()) {
+ if (_observers.empty())
+ return;
+ Iterator it(self);
+ id observer;
+ while ((observer = it.GetNext()) != nil)
callback(observer);
- }
+}
+
+#pragma mark - Private
+
+- (void)compact {
+ DCHECK(!_invocationDepth);
+ _observers.erase(std::remove(_observers.begin(), _observers.end(), nil),
+ _observers.end());
}
@end

Powered by Google App Engine
This is Rietveld 408576698