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 return nil; 90} 91 92- (id)initWithProtocol:(Protocol*)protocol { 93 self = [super init]; 94 if (self) { 95 _protocol = protocol; 96 } 97 return self; 98} 99 100- (Protocol*)protocol { 101 return _protocol; 102} 103 104- (void)addObserver:(id)observer { 105 DCHECK(observer); 106 DCHECK([observer conformsToProtocol:self.protocol]); 107 108 if (base::Contains(_observers, observer)) 109 return; 110 111 _observers.push_back(observer); 112} 113 114- (void)removeObserver:(id)observer { 115 DCHECK(observer); 116 auto it = base::ranges::find(_observers, observer); 117 if (it != _observers.end()) { 118 if (_invocationDepth) 119 *it = nil; 120 else 121 _observers.erase(it); 122 } 123} 124 125- (BOOL)empty { 126 int count = 0; 127 for (id observer : _observers) { 128 if (observer != nil) 129 ++count; 130 } 131 return count == 0; 132} 133 134#pragma mark - NSObject 135 136- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector { 137 NSMethodSignature* signature = [super methodSignatureForSelector:selector]; 138 if (signature) 139 return signature; 140 141 // Look for a required method in the protocol. protocol_getMethodDescription 142 // returns a struct whose fields are null if a method for the selector was 143 // not found. 144 struct objc_method_description description = 145 protocol_getMethodDescription(self.protocol, selector, YES, YES); 146 if (description.types) 147 return [NSMethodSignature signatureWithObjCTypes:description.types]; 148 149 // Look for an optional method in the protocol. 150 description = protocol_getMethodDescription(self.protocol, selector, NO, YES); 151 if (description.types) 152 return [NSMethodSignature signatureWithObjCTypes:description.types]; 153 154 // There is neither a required nor optional method with this selector in the 155 // protocol, so invoke -[NSObject doesNotRecognizeSelector:] to raise 156 // NSInvalidArgumentException. 157 [self doesNotRecognizeSelector:selector]; 158 return nil; 159} 160 161- (void)forwardInvocation:(NSInvocation*)invocation { 162 DCHECK(invocation); 163 if (_observers.empty()) 164 return; 165 SEL selector = [invocation selector]; 166 Iterator it(self); 167 id observer; 168 while ((observer = it.GetNext()) != nil) { 169 if ([observer respondsToSelector:selector]) 170 [invocation invokeWithTarget:observer]; 171 } 172} 173 174- (void)executeOnObservers:(ExecutionWithObserverBlock)callback { 175 DCHECK(callback); 176 if (_observers.empty()) 177 return; 178 Iterator it(self); 179 id observer; 180 while ((observer = it.GetNext()) != nil) 181 callback(observer); 182} 183 184#pragma mark - Private 185 186- (void)compact { 187 DCHECK(!_invocationDepth); 188 _observers.erase(std::remove(_observers.begin(), _observers.end(), nil), 189 _observers.end()); 190} 191 192@end 193