// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #import "base/ios/crb_protocol_observers.h" #include #include #include #include #include "base/logging.h" #include "base/mac/scoped_nsobject.h" #include "base/stl_util.h" @interface CRBProtocolObservers () { base::scoped_nsobject _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. - (id)initWithProtocol:(Protocol*)protocol; @end @implementation CRBProtocolObservers + (instancetype)observersWithProtocol:(Protocol*)protocol { return [[[self alloc] initWithProtocol:protocol] autorelease]; } - (id)init { NOTREACHED(); return nil; } - (id)initWithProtocol:(Protocol*)protocol { self = [super init]; if (self) { _protocol.reset([protocol retain]); } return self; } - (Protocol*)protocol { return _protocol.get(); } - (void)addObserver:(id)observer { DCHECK(observer); DCHECK([observer conformsToProtocol:self.protocol]); if (base::ContainsValue(_observers, observer)) return; _observers.push_back(observer); } - (void)removeObserver:(id)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 - (NSMethodSignature*)methodSignatureForSelector:(SEL)selector { NSMethodSignature* signature = [super methodSignatureForSelector:selector]; if (signature) return signature; // Look for a required method in the protocol. protocol_getMethodDescription // returns a struct whose fields are null if a method for the selector was // not found. struct objc_method_description description = protocol_getMethodDescription(self.protocol, selector, YES, YES); if (description.types) return [NSMethodSignature signatureWithObjCTypes:description.types]; // Look for an optional method in the protocol. description = protocol_getMethodDescription(self.protocol, selector, NO, YES); if (description.types) return [NSMethodSignature signatureWithObjCTypes:description.types]; // There is neither a required nor optional method with this selector in the // protocol, so invoke -[NSObject doesNotRecognizeSelector:] to raise // NSInvalidArgumentException. [self doesNotRecognizeSelector:selector]; return nil; } - (void)forwardInvocation:(NSInvocation*)invocation { DCHECK(invocation); if (_observers.empty()) return; SEL selector = [invocation selector]; Iterator it(self); id observer; while ((observer = it.GetNext()) != nil) { if ([observer respondsToSelector:selector]) [invocation invokeWithTarget:observer]; } } - (void)executeOnObservers:(ExecutionWithObserverBlock)callback { DCHECK(callback); 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