123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- //
- // A2DynamicDelegate.m
- // BlocksKit
- //
- #import <objc/message.h>
- #import "A2BlockInvocation.h"
- #import "A2DynamicDelegate.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);
- }
- @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];
- }
- - (id)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) || [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;
- }
|