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..90a277ba3f455fdb6a54c40ccaea4553296aca90 100644 |
--- a/base/ios/crb_protocol_observers.mm |
+++ b/base/ios/crb_protocol_observers.mm |
@@ -5,10 +5,69 @@ |
#import "base/ios/crb_protocol_observers.h" |
#include <objc/runtime.h> |
+#include <algorithm> |
+#include <vector> |
#include "base/logging.h" |
#include "base/mac/scoped_nsobject.h" |
+@interface CRBProtocolObservers () { |
+ base::scoped_nsobject<Protocol> _protocol; |
+ // ivars declared here are private to the implementation but must be |
+ // public for allowing the C++ |Iterator| class access to those ivars. |
+ @public |
+ // vector of weak pointers to observers. |
+ std::vector<__unsafe_unretained id> _observers; |
+ // The nested level of observer iteration. |
+ // A depth of 0 means nobody is currently iterating on the list of observers. |
+ int _invocationDepth; |
+} |
+ |
+// Removes nil observers from the list and is called when the |
+// |_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 nil elements. |
+ 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 +75,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 +90,6 @@ |
self = [super init]; |
if (self) { |
_protocol.reset([protocol retain]); |
- _observers.reset([[NSHashTable weakObjectsHashTable] retain]); |
} |
return self; |
} |
@@ -44,12 +99,34 @@ |
} |
- (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 { |
+ int count = 0; |
+ for (id observer : _observers) { |
+ if (observer != nil) |
+ ++count; |
+ } |
+ return count == 0; |
} |
#pragma mark - NSObject |
@@ -80,9 +157,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 +171,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 |