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