Chromium Code Reviews| 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..60fe5f9f48951eab147026caf6d5e9f2b79a7b8c 100644 |
| --- a/base/ios/crb_protocol_observers.mm |
| +++ b/base/ios/crb_protocol_observers.mm |
| @@ -5,10 +5,60 @@ |
| #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; |
| + @public |
| + std::vector<__weak id> _observers; |
|
sdefresne
2015/06/04 10:45:34
According to https://developer.apple.com/library/i
jbbegue
2015/06/04 12:13:03
Done.
|
| + int _invocation_depth; |
|
sdefresne
2015/06/04 10:18:34
_invocationDepth
jbbegue
2015/06/04 12:13:02
Done.
|
| +} |
| + |
| +- (void)compact; |
| + |
| +@end |
| + |
| +namespace { |
| + |
| +class Iterator { |
| + public: |
| + explicit Iterator(CRBProtocolObservers* protocolObservers); |
| + ~Iterator(); |
| + id GetNext(); |
| + |
| + private: |
| + CRBProtocolObservers* protocolObservers_ = nullptr; |
|
sdefresne
2015/06/04 10:18:34
protocol_observers_
jbbegue
2015/06/04 12:13:03
Done.
|
| + size_t index_ = 0; |
| + size_t max_index_; |
| +}; |
| + |
| +Iterator::Iterator(CRBProtocolObservers* protocolObservers) |
| + : protocolObservers_(protocolObservers), |
| + index_(0), |
| + max_index_(protocolObservers->_observers.size()) { |
| + ++protocolObservers->_invocation_depth; |
| +} |
| + |
| +Iterator::~Iterator() { |
| + if (protocolObservers_ && --protocolObservers_->_invocation_depth == 0) |
| + [protocolObservers_ compact]; |
| +} |
| + |
| +id Iterator::GetNext() { |
| + if (!protocolObservers_) |
| + return nil; |
| + auto& observers = protocolObservers_->_observers; |
| + // Skip null elements. |
| + size_t max_index = std::min(max_index_, observers.size()); |
| + while (index_ < max_index && !observers[index_]) |
| + ++index_; |
| + return index_ < max_index ? observers[index_++] : nullptr; |
|
sdefresne
2015/06/04 10:18:34
s/nullptr/nil/
jbbegue
2015/06/04 12:13:03
Done.
|
| +} |
| +} |
| + |
| @interface CRBProtocolObservers () |
| // Designated initializer. |
| @@ -16,12 +66,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 +81,6 @@ |
| self = [super init]; |
| if (self) { |
| _protocol.reset([protocol retain]); |
| - _observers.reset([[NSHashTable weakObjectsHashTable] retain]); |
| } |
| return self; |
| } |
| @@ -44,12 +90,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 (_invocation_depth) |
| + *it = nullptr; |
|
sdefresne
2015/06/04 10:18:34
s/nullptr/nil/
jbbegue
2015/06/04 12:13:03
Done.
|
| + else |
| + _observers.erase(it); |
| + } |
| +} |
| + |
| +- (BOOL)empty { |
| + return _observers.empty(); |
| } |
| #pragma mark - NSObject |
| @@ -80,9 +143,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()) != nullptr) { |
|
sdefresne
2015/06/04 10:18:34
s/nullptr/ or even remove the test
jbbegue
2015/06/04 12:13:03
Done.
|
| if ([observer respondsToSelector:selector]) |
| [invocation invokeWithTarget:observer]; |
| } |
| @@ -90,10 +157,19 @@ |
| - (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()) != nullptr) |
|
sdefresne
2015/06/04 10:18:34
s/nullptr/ or even remove the test
jbbegue
2015/06/04 12:13:02
Done.
|
| callback(observer); |
| - } |
| +} |
| + |
| +#pragma mark - Private |
| + |
| +- (void)compact { |
| + _observers.erase(std::remove(_observers.begin(), _observers.end(), nullptr), |
|
sdefresne
2015/06/04 10:18:34
s/nullptr/nil
jbbegue
2015/06/04 12:13:03
Done.
|
| + _observers.end()); |
| } |
| @end |