A2DynamicDelegate.m 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. //
  2. // A2DynamicDelegate.m
  3. // BlocksKit
  4. //
  5. #import <objc/message.h>
  6. #import "A2BlockInvocation.h"
  7. #import "A2DynamicDelegate.h"
  8. Protocol *a2_dataSourceProtocol(Class cls);
  9. Protocol *a2_delegateProtocol(Class cls);
  10. Protocol *a2_protocolForDelegatingObject(id obj, Protocol *protocol);
  11. static BOOL selectorsEqual(const void *item1, const void *item2, NSUInteger(*__unused size)(const void __unused *item))
  12. {
  13. return sel_isEqual((SEL)item1, (SEL)item2);
  14. }
  15. static NSString *selectorDescribe(const void *item1)
  16. {
  17. return NSStringFromSelector((SEL)item1);
  18. }
  19. @interface NSMapTable (BKAdditions)
  20. + (instancetype)bk_selectorsToStrongObjectsMapTable;
  21. - (id)bk_objectForSelector:(SEL)aSEL;
  22. - (void)bk_removeObjectForSelector:(SEL)aSEL;
  23. - (void)bk_setObject:(id)anObject forSelector:(SEL)aSEL;
  24. @end
  25. @implementation NSMapTable (BKAdditions)
  26. + (instancetype)bk_selectorsToStrongObjectsMapTable
  27. {
  28. NSPointerFunctions *selectors = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsOpaqueMemory|NSPointerFunctionsOpaquePersonality];
  29. selectors.isEqualFunction = selectorsEqual;
  30. selectors.descriptionFunction = selectorDescribe;
  31. NSPointerFunctions *strongObjects = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPersonality];
  32. return [[NSMapTable alloc] initWithKeyPointerFunctions:selectors valuePointerFunctions:strongObjects capacity:1];
  33. }
  34. - (id)bk_objectForSelector:(SEL)aSEL
  35. {
  36. void *selAsPtr = aSEL;
  37. return [self objectForKey:(__bridge id)selAsPtr];
  38. }
  39. - (void)bk_removeObjectForSelector:(SEL)aSEL
  40. {
  41. void *selAsPtr = aSEL;
  42. [self removeObjectForKey:(__bridge id)selAsPtr];
  43. }
  44. - (void)bk_setObject:(id)anObject forSelector:(SEL)aSEL
  45. {
  46. void *selAsPtr = aSEL;
  47. [self setObject:anObject forKey:(__bridge id)selAsPtr];
  48. }
  49. @end
  50. @interface A2DynamicClassDelegate : A2DynamicDelegate
  51. @property (nonatomic) Class proxiedClass;
  52. @end
  53. #pragma mark -
  54. @interface A2DynamicDelegate ()
  55. @property (nonatomic) A2DynamicClassDelegate *classProxy;
  56. @property (nonatomic, readonly) NSMapTable *invocationsBySelectors;
  57. @property (nonatomic, weak, readwrite) id realDelegate;
  58. - (BOOL) isClassProxy;
  59. @end
  60. @implementation A2DynamicDelegate
  61. - (A2DynamicClassDelegate *)classProxy
  62. {
  63. if (!_classProxy)
  64. {
  65. _classProxy = [[A2DynamicClassDelegate alloc] initWithProtocol:self.protocol];
  66. _classProxy.proxiedClass = object_getClass(self);
  67. }
  68. return _classProxy;
  69. }
  70. - (BOOL)isClassProxy
  71. {
  72. return NO;
  73. }
  74. - (Class)class
  75. {
  76. Class myClass = object_getClass(self);
  77. if (myClass == [A2DynamicDelegate class] || [myClass superclass] == [A2DynamicDelegate class])
  78. return (Class)self.classProxy;
  79. return [super class];
  80. }
  81. - (id)initWithProtocol:(Protocol *)protocol
  82. {
  83. _protocol = protocol;
  84. _handlers = [NSMutableDictionary dictionary];
  85. _invocationsBySelectors = [NSMapTable bk_selectorsToStrongObjectsMapTable];
  86. return self;
  87. }
  88. - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
  89. {
  90. A2BlockInvocation *invocation = nil;
  91. if ((invocation = [self.invocationsBySelectors bk_objectForSelector:aSelector]))
  92. return invocation.methodSignature;
  93. else if ([self.realDelegate methodSignatureForSelector:aSelector])
  94. return [self.realDelegate methodSignatureForSelector:aSelector];
  95. else if (class_respondsToSelector(object_getClass(self), aSelector))
  96. return [object_getClass(self) methodSignatureForSelector:aSelector];
  97. return [[NSObject class] methodSignatureForSelector:aSelector];
  98. }
  99. + (NSString *)description
  100. {
  101. return @"A2DynamicDelegate";
  102. }
  103. - (NSString *)description
  104. {
  105. return [NSString stringWithFormat:@"<A2DynamicDelegate:%p; protocol = %@>", (__bridge void *)self, NSStringFromProtocol(self.protocol)];
  106. }
  107. - (void)forwardInvocation:(NSInvocation *)outerInv
  108. {
  109. SEL selector = outerInv.selector;
  110. A2BlockInvocation *innerInv = nil;
  111. if ((innerInv = [self.invocationsBySelectors bk_objectForSelector:selector])) {
  112. [innerInv invokeWithInvocation:outerInv];
  113. } else if ([self.realDelegate respondsToSelector:selector]) {
  114. [outerInv invokeWithTarget:self.realDelegate];
  115. }
  116. }
  117. #pragma mark -
  118. - (BOOL)conformsToProtocol:(Protocol *)aProtocol
  119. {
  120. return protocol_isEqual(aProtocol, self.protocol) || [super conformsToProtocol:aProtocol];
  121. }
  122. - (BOOL)respondsToSelector:(SEL)selector
  123. {
  124. return [self.invocationsBySelectors bk_objectForSelector:selector] || class_respondsToSelector(object_getClass(self), selector) || [self.realDelegate respondsToSelector:selector];
  125. }
  126. - (void)doesNotRecognizeSelector:(SEL)aSelector
  127. {
  128. [NSException raise:NSInvalidArgumentException format:@"-[%s %@]: unrecognized selector sent to instance %p", object_getClassName(self), NSStringFromSelector(aSelector), (__bridge void *)self];
  129. }
  130. #pragma mark - Block Instance Method Implementations
  131. - (id)blockImplementationForMethod:(SEL)selector
  132. {
  133. A2BlockInvocation *invocation = nil;
  134. if ((invocation = [self.invocationsBySelectors bk_objectForSelector:selector]))
  135. return invocation.block;
  136. return NULL;
  137. }
  138. - (void)implementMethod:(SEL)selector withBlock:(id)block
  139. {
  140. NSCAssert(selector, @"Attempt to implement or remove NULL selector");
  141. BOOL isClassMethod = self.isClassProxy;
  142. if (!block) {
  143. [self.invocationsBySelectors bk_removeObjectForSelector:selector];
  144. return;
  145. }
  146. struct objc_method_description methodDescription = protocol_getMethodDescription(self.protocol, selector, YES, !isClassMethod);
  147. if (!methodDescription.name) methodDescription = protocol_getMethodDescription(self.protocol, selector, NO, !isClassMethod);
  148. A2BlockInvocation *inv = nil;
  149. if (methodDescription.name) {
  150. NSMethodSignature *protoSig = [NSMethodSignature signatureWithObjCTypes:methodDescription.types];
  151. inv = [[A2BlockInvocation alloc] initWithBlock:block methodSignature:protoSig];
  152. } else {
  153. inv = [[A2BlockInvocation alloc] initWithBlock:block];
  154. }
  155. [self.invocationsBySelectors bk_setObject:inv forSelector:selector];
  156. }
  157. - (void)removeBlockImplementationForMethod:(SEL)selector __unused
  158. {
  159. [self implementMethod:selector withBlock:nil];
  160. }
  161. #pragma mark - Block Class Method Implementations
  162. - (id)blockImplementationForClassMethod:(SEL)selector
  163. {
  164. return [self.classProxy blockImplementationForMethod:selector];
  165. }
  166. - (void)implementClassMethod:(SEL)selector withBlock:(id)block
  167. {
  168. [self.classProxy implementMethod:selector withBlock:block];
  169. }
  170. - (void)removeBlockImplementationForClassMethod:(SEL)selector __unused
  171. {
  172. [self.classProxy implementMethod:selector withBlock:nil];
  173. }
  174. @end
  175. #pragma mark -
  176. @implementation A2DynamicClassDelegate
  177. - (BOOL)isClassProxy
  178. {
  179. return YES;
  180. }
  181. - (BOOL)isEqual:(id)object
  182. {
  183. return [super isEqual:object] || [_proxiedClass isEqual:object];
  184. }
  185. - (BOOL)respondsToSelector:(SEL)aSelector
  186. {
  187. return [self.invocationsBySelectors bk_objectForSelector:aSelector] || [_proxiedClass respondsToSelector:aSelector];
  188. }
  189. - (Class)class
  190. {
  191. return self.proxiedClass;
  192. }
  193. - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
  194. {
  195. A2BlockInvocation *invocation = nil;
  196. if ((invocation = [self.invocationsBySelectors bk_objectForSelector:aSelector]))
  197. return invocation.methodSignature;
  198. else if ([_proxiedClass methodSignatureForSelector:aSelector])
  199. return [_proxiedClass methodSignatureForSelector:aSelector];
  200. return [[NSObject class] methodSignatureForSelector:aSelector];
  201. }
  202. - (NSString *)description
  203. {
  204. return [_proxiedClass description];
  205. }
  206. - (NSUInteger)hash
  207. {
  208. return [_proxiedClass hash];
  209. }
  210. - (void)forwardInvocation:(NSInvocation *)outerInv
  211. {
  212. SEL selector = outerInv.selector;
  213. A2BlockInvocation *innerInv = nil;
  214. if ((innerInv = [self.invocationsBySelectors bk_objectForSelector:selector])) {
  215. [innerInv invokeWithInvocation:outerInv];
  216. } else {
  217. [outerInv invokeWithTarget:_proxiedClass];
  218. }
  219. }
  220. #pragma mark - Unavailable Methods
  221. - (id)blockImplementationForClassMethod:(SEL)selector
  222. {
  223. [self doesNotRecognizeSelector:_cmd];
  224. return nil;
  225. }
  226. - (void)implementClassMethod:(SEL)selector withBlock:(id)block
  227. {
  228. [self doesNotRecognizeSelector:_cmd];
  229. }
  230. - (void)removeBlockImplementationForClassMethod:(SEL)selector
  231. {
  232. [self doesNotRecognizeSelector:_cmd];
  233. }
  234. @end
  235. #pragma mark - Helper functions
  236. static Protocol *a2_classProtocol(Class _cls, NSString *suffix, NSString *description)
  237. {
  238. Class cls = _cls;
  239. while (cls) {
  240. NSString *className = NSStringFromClass(cls);
  241. NSString *protocolName = [className stringByAppendingString:suffix];
  242. Protocol *protocol = objc_getProtocol(protocolName.UTF8String);
  243. if (protocol) return protocol;
  244. cls = class_getSuperclass(cls);
  245. }
  246. NSCAssert(NO, @"Specify protocol explicitly: could not determine %@ protocol for class %@ (tried <%@>)", description, NSStringFromClass(_cls), [NSStringFromClass(_cls) stringByAppendingString:suffix]);
  247. return nil;
  248. }
  249. Protocol *a2_dataSourceProtocol(Class cls)
  250. {
  251. return a2_classProtocol(cls, @"DataSource", @"data source");
  252. }
  253. Protocol *a2_delegateProtocol(Class cls)
  254. {
  255. return a2_classProtocol(cls, @"Delegate", @"delegate");
  256. }
  257. Protocol *a2_protocolForDelegatingObject(id obj, Protocol *protocol)
  258. {
  259. NSString *protocolName = NSStringFromProtocol(protocol);
  260. if ([protocolName hasSuffix:@"Delegate"]) {
  261. Protocol *p = a2_delegateProtocol([obj class]);
  262. if (p) return p;
  263. } else if ([protocolName hasSuffix:@"DataSource"]) {
  264. Protocol *p = a2_dataSourceProtocol([obj class]);
  265. if (p) return p;
  266. }
  267. return protocol;
  268. }