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