crb_protocol_observers.mm 4.8 KB

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