• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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