cdts/xdts-ios 3/TreeHole/Code/Utility/ImagePicker/BlocksKit/DynamicDelegate/A2DynamicDelegate.m

351 lines
9.6 KiB
Mathematica
Raw Permalink Normal View History

2023-07-27 09:20:00 +08:00
//
// A2DynamicDelegate.m
// BlocksKit
//
#import "A2DynamicDelegate.h"
@import ObjectiveC.message;
@import ObjectiveC.runtime;
#import "A2BlockInvocation.h"
Protocol *a2_dataSourceProtocol(Class cls);
Protocol *a2_delegateProtocol(Class cls);
Protocol *a2_protocolForDelegatingObject(id obj, Protocol *protocol);
static BOOL selectorsEqual(const void *item1, const void *item2, NSUInteger(*__unused size)(const void __unused *item))
{
return sel_isEqual((SEL)item1, (SEL)item2);
}
static NSString *selectorDescribe(const void *item1)
{
return NSStringFromSelector((SEL)item1);
}
static inline BOOL protocol_declaredSelector(Protocol *protocol, SEL selector)
{
for (int i = 0; i < 4; i++) {
BOOL required = 1 & (i);
BOOL instance = 1 & (i >> 1);
struct objc_method_description description = protocol_getMethodDescription(protocol, selector, required, instance);
if (description.name) {
return YES;
}
}
return NO;
}
@interface NSMapTable (BKAdditions)
+ (instancetype)bk_selectorsToStrongObjectsMapTable;
- (id)bk_objectForSelector:(SEL)aSEL;
- (void)bk_removeObjectForSelector:(SEL)aSEL;
- (void)bk_setObject:(id)anObject forSelector:(SEL)aSEL;
@end
@implementation NSMapTable (BKAdditions)
+ (instancetype)bk_selectorsToStrongObjectsMapTable
{
NSPointerFunctions *selectors = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsOpaqueMemory|NSPointerFunctionsOpaquePersonality];
selectors.isEqualFunction = selectorsEqual;
selectors.descriptionFunction = selectorDescribe;
NSPointerFunctions *strongObjects = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPersonality];
return [[NSMapTable alloc] initWithKeyPointerFunctions:selectors valuePointerFunctions:strongObjects capacity:1];
}
- (id)bk_objectForSelector:(SEL)aSEL
{
void *selAsPtr = aSEL;
return [self objectForKey:(__bridge id)selAsPtr];
}
- (void)bk_removeObjectForSelector:(SEL)aSEL
{
void *selAsPtr = aSEL;
[self removeObjectForKey:(__bridge id)selAsPtr];
}
- (void)bk_setObject:(id)anObject forSelector:(SEL)aSEL
{
void *selAsPtr = aSEL;
[self setObject:anObject forKey:(__bridge id)selAsPtr];
}
@end
@interface A2DynamicClassDelegate : A2DynamicDelegate
@property (nonatomic) Class proxiedClass;
@end
#pragma mark -
@interface A2DynamicDelegate ()
@property (nonatomic) A2DynamicClassDelegate *classProxy;
@property (nonatomic, readonly) NSMapTable *invocationsBySelectors;
@property (nonatomic, weak, readwrite) id realDelegate;
- (BOOL) isClassProxy;
@end
@implementation A2DynamicDelegate
- (A2DynamicClassDelegate *)classProxy
{
if (!_classProxy)
{
_classProxy = [[A2DynamicClassDelegate alloc] initWithProtocol:self.protocol];
_classProxy.proxiedClass = object_getClass(self);
}
return _classProxy;
}
- (BOOL)isClassProxy
{
return NO;
}
- (Class)class
{
Class myClass = object_getClass(self);
if (myClass == [A2DynamicDelegate class] || [myClass superclass] == [A2DynamicDelegate class])
return (Class)self.classProxy;
return [super class];
}
- (instancetype)initWithProtocol:(Protocol *)protocol
{
_protocol = protocol;
_handlers = [NSMutableDictionary dictionary];
_invocationsBySelectors = [NSMapTable bk_selectorsToStrongObjectsMapTable];
return self;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
A2BlockInvocation *invocation = nil;
if ((invocation = [self.invocationsBySelectors bk_objectForSelector:aSelector]))
return invocation.methodSignature;
else if ([self.realDelegate methodSignatureForSelector:aSelector])
return [self.realDelegate methodSignatureForSelector:aSelector];
else if (class_respondsToSelector(object_getClass(self), aSelector))
return [object_getClass(self) methodSignatureForSelector:aSelector];
return [[NSObject class] methodSignatureForSelector:aSelector];
}
+ (NSString *)description
{
return @"A2DynamicDelegate";
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<A2DynamicDelegate:%p; protocol = %@>", (__bridge void *)self, NSStringFromProtocol(self.protocol)];
}
- (void)forwardInvocation:(NSInvocation *)outerInv
{
SEL selector = outerInv.selector;
A2BlockInvocation *innerInv = nil;
if ((innerInv = [self.invocationsBySelectors bk_objectForSelector:selector])) {
[innerInv invokeWithInvocation:outerInv];
} else if ([self.realDelegate respondsToSelector:selector]) {
[outerInv invokeWithTarget:self.realDelegate];
}
}
#pragma mark -
- (BOOL)conformsToProtocol:(Protocol *)aProtocol
{
return protocol_isEqual(aProtocol, self.protocol) || [super conformsToProtocol:aProtocol];
}
- (BOOL)respondsToSelector:(SEL)selector
{
return [self.invocationsBySelectors bk_objectForSelector:selector] ||
class_respondsToSelector(object_getClass(self), selector) ||
(protocol_declaredSelector(self.protocol, selector) && [self.realDelegate respondsToSelector:selector]);
}
- (void)doesNotRecognizeSelector:(SEL)aSelector
{
[NSException raise:NSInvalidArgumentException format:@"-[%s %@]: unrecognized selector sent to instance %p", object_getClassName(self), NSStringFromSelector(aSelector), (__bridge void *)self];
}
#pragma mark - Block Instance Method Implementations
- (id)blockImplementationForMethod:(SEL)selector
{
A2BlockInvocation *invocation = nil;
if ((invocation = [self.invocationsBySelectors bk_objectForSelector:selector]))
return invocation.block;
return NULL;
}
- (void)implementMethod:(SEL)selector withBlock:(id)block
{
NSCAssert(selector, @"Attempt to implement or remove NULL selector");
BOOL isClassMethod = self.isClassProxy;
if (!block) {
[self.invocationsBySelectors bk_removeObjectForSelector:selector];
return;
}
struct objc_method_description methodDescription = protocol_getMethodDescription(self.protocol, selector, YES, !isClassMethod);
if (!methodDescription.name) methodDescription = protocol_getMethodDescription(self.protocol, selector, NO, !isClassMethod);
A2BlockInvocation *inv = nil;
if (methodDescription.name) {
NSMethodSignature *protoSig = [NSMethodSignature signatureWithObjCTypes:methodDescription.types];
inv = [[A2BlockInvocation alloc] initWithBlock:block methodSignature:protoSig];
} else {
inv = [[A2BlockInvocation alloc] initWithBlock:block];
}
[self.invocationsBySelectors bk_setObject:inv forSelector:selector];
}
- (void)removeBlockImplementationForMethod:(SEL)selector __unused
{
[self implementMethod:selector withBlock:nil];
}
#pragma mark - Block Class Method Implementations
- (id)blockImplementationForClassMethod:(SEL)selector
{
return [self.classProxy blockImplementationForMethod:selector];
}
- (void)implementClassMethod:(SEL)selector withBlock:(id)block
{
[self.classProxy implementMethod:selector withBlock:block];
}
- (void)removeBlockImplementationForClassMethod:(SEL)selector __unused
{
[self.classProxy implementMethod:selector withBlock:nil];
}
@end
#pragma mark -
@implementation A2DynamicClassDelegate
- (BOOL)isClassProxy
{
return YES;
}
- (BOOL)isEqual:(id)object
{
return [super isEqual:object] || [_proxiedClass isEqual:object];
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
return [self.invocationsBySelectors bk_objectForSelector:aSelector] || [_proxiedClass respondsToSelector:aSelector];
}
- (Class)class
{
return self.proxiedClass;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
A2BlockInvocation *invocation = nil;
if ((invocation = [self.invocationsBySelectors bk_objectForSelector:aSelector]))
return invocation.methodSignature;
else if ([_proxiedClass methodSignatureForSelector:aSelector])
return [_proxiedClass methodSignatureForSelector:aSelector];
return [[NSObject class] methodSignatureForSelector:aSelector];
}
- (NSString *)description
{
return [_proxiedClass description];
}
- (NSUInteger)hash
{
return [_proxiedClass hash];
}
- (void)forwardInvocation:(NSInvocation *)outerInv
{
SEL selector = outerInv.selector;
A2BlockInvocation *innerInv = nil;
if ((innerInv = [self.invocationsBySelectors bk_objectForSelector:selector])) {
[innerInv invokeWithInvocation:outerInv];
} else {
[outerInv invokeWithTarget:_proxiedClass];
}
}
#pragma mark - Unavailable Methods
- (id)blockImplementationForClassMethod:(SEL)selector
{
[self doesNotRecognizeSelector:_cmd];
return nil;
}
- (void)implementClassMethod:(SEL)selector withBlock:(id)block
{
[self doesNotRecognizeSelector:_cmd];
}
- (void)removeBlockImplementationForClassMethod:(SEL)selector
{
[self doesNotRecognizeSelector:_cmd];
}
@end
#pragma mark - Helper functions
static Protocol *a2_classProtocol(Class _cls, NSString *suffix, NSString *description)
{
Class cls = _cls;
while (cls) {
NSString *className = NSStringFromClass(cls);
NSString *protocolName = [className stringByAppendingString:suffix];
Protocol *protocol = objc_getProtocol(protocolName.UTF8String);
if (protocol) return protocol;
cls = class_getSuperclass(cls);
}
NSCAssert(NO, @"Specify protocol explicitly: could not determine %@ protocol for class %@ (tried <%@>)", description, NSStringFromClass(_cls), [NSStringFromClass(_cls) stringByAppendingString:suffix]);
return nil;
}
Protocol *a2_dataSourceProtocol(Class cls)
{
return a2_classProtocol(cls, @"DataSource", @"data source");
}
Protocol *a2_delegateProtocol(Class cls)
{
return a2_classProtocol(cls, @"Delegate", @"delegate");
}
Protocol *a2_protocolForDelegatingObject(id obj, Protocol *protocol)
{
NSString *protocolName = NSStringFromProtocol(protocol);
if ([protocolName hasSuffix:@"Delegate"]) {
Protocol *p = a2_delegateProtocol([obj class]);
if (p) return p;
} else if ([protocolName hasSuffix:@"DataSource"]) {
Protocol *p = a2_dataSourceProtocol([obj class]);
if (p) return p;
}
return protocol;
}