blob: 8c4172a345e32bf98b7796c0084255596268dff1 [file] [log] [blame]
stuartmorgan4733f552015-02-14 22:19:301// 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>
avi9b6f42932015-12-26 22:15:148#include <stddef.h>
jbbegue12d50e72015-06-05 08:12:439#include <algorithm>
10#include <vector>
stuartmorgan4733f552015-02-14 22:19:3011
12#include "base/logging.h"
13#include "base/mac/scoped_nsobject.h"
thestig6c335d42015-12-07 18:25:3414#include "base/stl_util.h"
stuartmorgan4733f552015-02-14 22:19:3015
jbbegue12d50e72015-06-05 08:12:4316@interface CRBProtocolObservers () {
17 base::scoped_nsobject<Protocol> _protocol;
18 // ivars declared here are private to the implementation but must be
19 // public for allowing the C++ |Iterator| class access to those ivars.
20 @public
21 // vector of weak pointers to observers.
22 std::vector<__unsafe_unretained id> _observers;
23 // The nested level of observer iteration.
24 // A depth of 0 means nobody is currently iterating on the list of observers.
25 int _invocationDepth;
26}
27
28// Removes nil observers from the list and is called when the
29// |_invocationDepth| reaches 0.
30- (void)compact;
31
32@end
33
34namespace {
35
36class Iterator {
37 public:
38 explicit Iterator(CRBProtocolObservers* protocol_observers);
39 ~Iterator();
40 id GetNext();
41
42 private:
43 CRBProtocolObservers* protocol_observers_;
44 size_t index_;
45 size_t max_index_;
46};
47
48Iterator::Iterator(CRBProtocolObservers* protocol_observers)
49 : protocol_observers_(protocol_observers),
50 index_(0),
51 max_index_(protocol_observers->_observers.size()) {
52 DCHECK(protocol_observers_);
53 ++protocol_observers->_invocationDepth;
54}
55
56Iterator::~Iterator() {
57 if (protocol_observers_ && --protocol_observers_->_invocationDepth == 0)
58 [protocol_observers_ compact];
59}
60
61id Iterator::GetNext() {
62 if (!protocol_observers_)
63 return nil;
64 auto& observers = protocol_observers_->_observers;
65 // Skip nil elements.
66 size_t max_index = std::min(max_index_, observers.size());
67 while (index_ < max_index && !observers[index_])
68 ++index_;
69 return index_ < max_index ? observers[index_++] : nil;
70}
71}
72
stuartmorgan4733f552015-02-14 22:19:3073@interface CRBProtocolObservers ()
74
75// Designated initializer.
76- (id)initWithProtocol:(Protocol*)protocol;
77
78@end
79
jbbegue12d50e72015-06-05 08:12:4380@implementation CRBProtocolObservers
stuartmorgan4733f552015-02-14 22:19:3081
jbbegue12d50e72015-06-05 08:12:4382+ (instancetype)observersWithProtocol:(Protocol*)protocol {
stuartmorgan4733f552015-02-14 22:19:3083 return [[[self alloc] initWithProtocol:protocol] autorelease];
84}
85
86- (id)init {
87 NOTREACHED();
88 return nil;
89}
90
91- (id)initWithProtocol:(Protocol*)protocol {
92 self = [super init];
93 if (self) {
94 _protocol.reset([protocol retain]);
stuartmorgan4733f552015-02-14 22:19:3095 }
96 return self;
97}
98
99- (Protocol*)protocol {
100 return _protocol.get();
101}
102
103- (void)addObserver:(id)observer {
jbbegue12d50e72015-06-05 08:12:43104 DCHECK(observer);
stuartmorgan4733f552015-02-14 22:19:30105 DCHECK([observer conformsToProtocol:self.protocol]);
jbbegue12d50e72015-06-05 08:12:43106
thestig6c335d42015-12-07 18:25:34107 if (ContainsValue(_observers, observer))
jbbegue12d50e72015-06-05 08:12:43108 return;
109
110 _observers.push_back(observer);
stuartmorgan4733f552015-02-14 22:19:30111}
112
113- (void)removeObserver:(id)observer {
jbbegue12d50e72015-06-05 08:12:43114 DCHECK(observer);
115 auto it = std::find(_observers.begin(), _observers.end(), 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;
stuartmorgan4733f552015-02-14 22:19:30131}
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 {
jbbegue12d50e72015-06-05 08:12:43161 DCHECK(invocation);
162 if (_observers.empty())
163 return;
stuartmorgan4733f552015-02-14 22:19:30164 SEL selector = [invocation selector];
jbbegue12d50e72015-06-05 08:12:43165 Iterator it(self);
166 id observer;
167 while ((observer = it.GetNext()) != nil) {
stuartmorgan4733f552015-02-14 22:19:30168 if ([observer respondsToSelector:selector])
169 [invocation invokeWithTarget:observer];
170 }
171}
172
173- (void)executeOnObservers:(ExecutionWithObserverBlock)callback {
174 DCHECK(callback);
jbbegue12d50e72015-06-05 08:12:43175 if (_observers.empty())
176 return;
177 Iterator it(self);
178 id observer;
179 while ((observer = it.GetNext()) != nil)
stuartmorgan4733f552015-02-14 22:19:30180 callback(observer);
jbbegue12d50e72015-06-05 08:12:43181}
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());
stuartmorgan4733f552015-02-14 22:19:30189}
190
191@end