blob: 7260249e2c1c45f05015f5d070118affd4862d8e [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
Hans Wennborga47ddf82020-05-05 18:08:0712#include "base/check.h"
Jan Wilken Dörrieb5a41c32020-12-09 18:55:4713#include "base/containers/contains.h"
Hans Wennborga47ddf82020-05-05 18:08:0714#include "base/notreached.h"
stuartmorgan4733f552015-02-14 22:19:3015
Stepan Khapugin81f3a642021-02-26 11:42:1316#if !defined(__has_feature) || !__has_feature(objc_arc)
17#error "This file requires ARC support."
18#endif
19
jbbegue12d50e72015-06-05 08:12:4320@interface CRBProtocolObservers () {
Stepan Khapugin81f3a642021-02-26 11:42:1321 Protocol* _protocol;
jbbegue12d50e72015-06-05 08:12:4322 // ivars declared here are private to the implementation but must be
23 // public for allowing the C++ |Iterator| class access to those ivars.
24 @public
25 // vector of weak pointers to observers.
Stepan Khapugin81f3a642021-02-26 11:42:1326 std::vector<__weak id> _observers;
jbbegue12d50e72015-06-05 08:12:4327 // The nested level of observer iteration.
28 // A depth of 0 means nobody is currently iterating on the list of observers.
29 int _invocationDepth;
30}
31
32// Removes nil observers from the list and is called when the
33// |_invocationDepth| reaches 0.
34- (void)compact;
35
36@end
37
38namespace {
39
40class Iterator {
41 public:
42 explicit Iterator(CRBProtocolObservers* protocol_observers);
43 ~Iterator();
44 id GetNext();
45
46 private:
47 CRBProtocolObservers* protocol_observers_;
48 size_t index_;
49 size_t max_index_;
50};
51
52Iterator::Iterator(CRBProtocolObservers* protocol_observers)
53 : protocol_observers_(protocol_observers),
54 index_(0),
55 max_index_(protocol_observers->_observers.size()) {
56 DCHECK(protocol_observers_);
57 ++protocol_observers->_invocationDepth;
58}
59
60Iterator::~Iterator() {
61 if (protocol_observers_ && --protocol_observers_->_invocationDepth == 0)
62 [protocol_observers_ compact];
63}
64
65id Iterator::GetNext() {
66 if (!protocol_observers_)
67 return nil;
68 auto& observers = protocol_observers_->_observers;
69 // Skip nil elements.
70 size_t max_index = std::min(max_index_, observers.size());
71 while (index_ < max_index && !observers[index_])
72 ++index_;
73 return index_ < max_index ? observers[index_++] : nil;
74}
75}
76
stuartmorgan4733f552015-02-14 22:19:3077@interface CRBProtocolObservers ()
78
79// Designated initializer.
Stepan Khapugin81f3a642021-02-26 11:42:1380- (instancetype)initWithProtocol:(Protocol*)protocol;
stuartmorgan4733f552015-02-14 22:19:3081
82@end
83
jbbegue12d50e72015-06-05 08:12:4384@implementation CRBProtocolObservers
stuartmorgan4733f552015-02-14 22:19:3085
jbbegue12d50e72015-06-05 08:12:4386+ (instancetype)observersWithProtocol:(Protocol*)protocol {
Stepan Khapugin81f3a642021-02-26 11:42:1387 return [[self alloc] initWithProtocol:protocol];
stuartmorgan4733f552015-02-14 22:19:3088}
89
90- (id)init {
91 NOTREACHED();
92 return nil;
93}
94
95- (id)initWithProtocol:(Protocol*)protocol {
96 self = [super init];
97 if (self) {
Stepan Khapugin81f3a642021-02-26 11:42:1398 _protocol = protocol;
stuartmorgan4733f552015-02-14 22:19:3099 }
100 return self;
101}
102
103- (Protocol*)protocol {
Stepan Khapugin81f3a642021-02-26 11:42:13104 return _protocol;
stuartmorgan4733f552015-02-14 22:19:30105}
106
107- (void)addObserver:(id)observer {
jbbegue12d50e72015-06-05 08:12:43108 DCHECK(observer);
stuartmorgan4733f552015-02-14 22:19:30109 DCHECK([observer conformsToProtocol:self.protocol]);
jbbegue12d50e72015-06-05 08:12:43110
Jan Wilken Dörrief61e74c2019-06-07 08:20:02111 if (base::Contains(_observers, observer))
jbbegue12d50e72015-06-05 08:12:43112 return;
113
114 _observers.push_back(observer);
stuartmorgan4733f552015-02-14 22:19:30115}
116
117- (void)removeObserver:(id)observer {
jbbegue12d50e72015-06-05 08:12:43118 DCHECK(observer);
119 auto it = std::find(_observers.begin(), _observers.end(), observer);
120 if (it != _observers.end()) {
121 if (_invocationDepth)
122 *it = nil;
123 else
124 _observers.erase(it);
125 }
126}
127
128- (BOOL)empty {
129 int count = 0;
130 for (id observer : _observers) {
131 if (observer != nil)
132 ++count;
133 }
134 return count == 0;
stuartmorgan4733f552015-02-14 22:19:30135}
136
137#pragma mark - NSObject
138
139- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector {
140 NSMethodSignature* signature = [super methodSignatureForSelector:selector];
141 if (signature)
142 return signature;
143
144 // Look for a required method in the protocol. protocol_getMethodDescription
145 // returns a struct whose fields are null if a method for the selector was
146 // not found.
147 struct objc_method_description description =
148 protocol_getMethodDescription(self.protocol, selector, YES, YES);
149 if (description.types)
150 return [NSMethodSignature signatureWithObjCTypes:description.types];
151
152 // Look for an optional method in the protocol.
153 description = protocol_getMethodDescription(self.protocol, selector, NO, YES);
154 if (description.types)
155 return [NSMethodSignature signatureWithObjCTypes:description.types];
156
157 // There is neither a required nor optional method with this selector in the
158 // protocol, so invoke -[NSObject doesNotRecognizeSelector:] to raise
159 // NSInvalidArgumentException.
160 [self doesNotRecognizeSelector:selector];
161 return nil;
162}
163
164- (void)forwardInvocation:(NSInvocation*)invocation {
jbbegue12d50e72015-06-05 08:12:43165 DCHECK(invocation);
166 if (_observers.empty())
167 return;
stuartmorgan4733f552015-02-14 22:19:30168 SEL selector = [invocation selector];
jbbegue12d50e72015-06-05 08:12:43169 Iterator it(self);
170 id observer;
171 while ((observer = it.GetNext()) != nil) {
stuartmorgan4733f552015-02-14 22:19:30172 if ([observer respondsToSelector:selector])
173 [invocation invokeWithTarget:observer];
174 }
175}
176
177- (void)executeOnObservers:(ExecutionWithObserverBlock)callback {
178 DCHECK(callback);
jbbegue12d50e72015-06-05 08:12:43179 if (_observers.empty())
180 return;
181 Iterator it(self);
182 id observer;
183 while ((observer = it.GetNext()) != nil)
stuartmorgan4733f552015-02-14 22:19:30184 callback(observer);
jbbegue12d50e72015-06-05 08:12:43185}
186
187#pragma mark - Private
188
189- (void)compact {
190 DCHECK(!_invocationDepth);
191 _observers.erase(std::remove(_observers.begin(), _observers.end(), nil),
192 _observers.end());
stuartmorgan4733f552015-02-14 22:19:30193}
194
195@end