mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 06:16:30 +03:00
192 lines
4.8 KiB
Plaintext
192 lines
4.8 KiB
Plaintext
// Copyright 2014 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#import "base/ios/crb_protocol_observers.h"
|
|
|
|
#include <objc/runtime.h>
|
|
#include <stddef.h>
|
|
#include <algorithm>
|
|
#include <vector>
|
|
|
|
#include "base/logging.h"
|
|
#include "base/mac/scoped_nsobject.h"
|
|
#include "base/stl_util.h"
|
|
|
|
@interface CRBProtocolObservers () {
|
|
base::scoped_nsobject<Protocol> _protocol;
|
|
// ivars declared here are private to the implementation but must be
|
|
// public for allowing the C++ |Iterator| class access to those ivars.
|
|
@public
|
|
// vector of weak pointers to observers.
|
|
std::vector<__unsafe_unretained id> _observers;
|
|
// The nested level of observer iteration.
|
|
// A depth of 0 means nobody is currently iterating on the list of observers.
|
|
int _invocationDepth;
|
|
}
|
|
|
|
// Removes nil observers from the list and is called when the
|
|
// |_invocationDepth| reaches 0.
|
|
- (void)compact;
|
|
|
|
@end
|
|
|
|
namespace {
|
|
|
|
class Iterator {
|
|
public:
|
|
explicit Iterator(CRBProtocolObservers* protocol_observers);
|
|
~Iterator();
|
|
id GetNext();
|
|
|
|
private:
|
|
CRBProtocolObservers* protocol_observers_;
|
|
size_t index_;
|
|
size_t max_index_;
|
|
};
|
|
|
|
Iterator::Iterator(CRBProtocolObservers* protocol_observers)
|
|
: protocol_observers_(protocol_observers),
|
|
index_(0),
|
|
max_index_(protocol_observers->_observers.size()) {
|
|
DCHECK(protocol_observers_);
|
|
++protocol_observers->_invocationDepth;
|
|
}
|
|
|
|
Iterator::~Iterator() {
|
|
if (protocol_observers_ && --protocol_observers_->_invocationDepth == 0)
|
|
[protocol_observers_ compact];
|
|
}
|
|
|
|
id Iterator::GetNext() {
|
|
if (!protocol_observers_)
|
|
return nil;
|
|
auto& observers = protocol_observers_->_observers;
|
|
// Skip nil elements.
|
|
size_t max_index = std::min(max_index_, observers.size());
|
|
while (index_ < max_index && !observers[index_])
|
|
++index_;
|
|
return index_ < max_index ? observers[index_++] : nil;
|
|
}
|
|
}
|
|
|
|
@interface CRBProtocolObservers ()
|
|
|
|
// Designated initializer.
|
|
- (id)initWithProtocol:(Protocol*)protocol;
|
|
|
|
@end
|
|
|
|
@implementation CRBProtocolObservers
|
|
|
|
+ (instancetype)observersWithProtocol:(Protocol*)protocol {
|
|
return [[[self alloc] initWithProtocol:protocol] autorelease];
|
|
}
|
|
|
|
- (id)init {
|
|
NOTREACHED();
|
|
return nil;
|
|
}
|
|
|
|
- (id)initWithProtocol:(Protocol*)protocol {
|
|
self = [super init];
|
|
if (self) {
|
|
_protocol.reset([protocol retain]);
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (Protocol*)protocol {
|
|
return _protocol.get();
|
|
}
|
|
|
|
- (void)addObserver:(id)observer {
|
|
DCHECK(observer);
|
|
DCHECK([observer conformsToProtocol:self.protocol]);
|
|
|
|
if (base::ContainsValue(_observers, observer))
|
|
return;
|
|
|
|
_observers.push_back(observer);
|
|
}
|
|
|
|
- (void)removeObserver:(id)observer {
|
|
DCHECK(observer);
|
|
auto it = std::find(_observers.begin(), _observers.end(), observer);
|
|
if (it != _observers.end()) {
|
|
if (_invocationDepth)
|
|
*it = nil;
|
|
else
|
|
_observers.erase(it);
|
|
}
|
|
}
|
|
|
|
- (BOOL)empty {
|
|
int count = 0;
|
|
for (id observer : _observers) {
|
|
if (observer != nil)
|
|
++count;
|
|
}
|
|
return count == 0;
|
|
}
|
|
|
|
#pragma mark - NSObject
|
|
|
|
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector {
|
|
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
|
|
if (signature)
|
|
return signature;
|
|
|
|
// Look for a required method in the protocol. protocol_getMethodDescription
|
|
// returns a struct whose fields are null if a method for the selector was
|
|
// not found.
|
|
struct objc_method_description description =
|
|
protocol_getMethodDescription(self.protocol, selector, YES, YES);
|
|
if (description.types)
|
|
return [NSMethodSignature signatureWithObjCTypes:description.types];
|
|
|
|
// Look for an optional method in the protocol.
|
|
description = protocol_getMethodDescription(self.protocol, selector, NO, YES);
|
|
if (description.types)
|
|
return [NSMethodSignature signatureWithObjCTypes:description.types];
|
|
|
|
// There is neither a required nor optional method with this selector in the
|
|
// protocol, so invoke -[NSObject doesNotRecognizeSelector:] to raise
|
|
// NSInvalidArgumentException.
|
|
[self doesNotRecognizeSelector:selector];
|
|
return nil;
|
|
}
|
|
|
|
- (void)forwardInvocation:(NSInvocation*)invocation {
|
|
DCHECK(invocation);
|
|
if (_observers.empty())
|
|
return;
|
|
SEL selector = [invocation selector];
|
|
Iterator it(self);
|
|
id observer;
|
|
while ((observer = it.GetNext()) != nil) {
|
|
if ([observer respondsToSelector:selector])
|
|
[invocation invokeWithTarget:observer];
|
|
}
|
|
}
|
|
|
|
- (void)executeOnObservers:(ExecutionWithObserverBlock)callback {
|
|
DCHECK(callback);
|
|
if (_observers.empty())
|
|
return;
|
|
Iterator it(self);
|
|
id observer;
|
|
while ((observer = it.GetNext()) != nil)
|
|
callback(observer);
|
|
}
|
|
|
|
#pragma mark - Private
|
|
|
|
- (void)compact {
|
|
DCHECK(!_invocationDepth);
|
|
_observers.erase(std::remove(_observers.begin(), _observers.end(), nil),
|
|
_observers.end());
|
|
}
|
|
|
|
@end
|