NSObject+A2DynamicDelegate.m 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. //
  2. // NSObject+A2DynamicDelegate.m
  3. // BlocksKit
  4. //
  5. #import <objc/runtime.h>
  6. #import "NSObject+A2DynamicDelegate.h"
  7. extern Protocol *a2_dataSourceProtocol(Class cls);
  8. extern Protocol *a2_delegateProtocol(Class cls);
  9. static Class a2_dynamicDelegateClass(Class cls, NSString *suffix)
  10. {
  11. while (cls) {
  12. NSString *className = [NSString stringWithFormat:@"A2Dynamic%@%@", NSStringFromClass(cls), suffix];
  13. Class ddClass = NSClassFromString(className);
  14. if (ddClass) return ddClass;
  15. cls = class_getSuperclass(cls);
  16. }
  17. return [A2DynamicDelegate class];
  18. }
  19. static dispatch_queue_t a2_backgroundQueue(void)
  20. {
  21. static dispatch_once_t onceToken;
  22. static dispatch_queue_t backgroundQueue = nil;
  23. dispatch_once(&onceToken, ^{
  24. backgroundQueue = dispatch_queue_create("BlocksKit.DynamicDelegate.Queue", DISPATCH_QUEUE_SERIAL);
  25. });
  26. return backgroundQueue;
  27. }
  28. @implementation NSObject (A2DynamicDelegate)
  29. - (id)bk_dynamicDataSource
  30. {
  31. Protocol *protocol = a2_dataSourceProtocol([self class]);
  32. Class class = a2_dynamicDelegateClass([self class], @"DataSource");
  33. return [self bk_dynamicDelegateWithClass:class forProtocol:protocol];
  34. }
  35. - (id)bk_dynamicDelegate
  36. {
  37. Protocol *protocol = a2_delegateProtocol([self class]);
  38. Class class = a2_dynamicDelegateClass([self class], @"Delegate");
  39. return [self bk_dynamicDelegateWithClass:class forProtocol:protocol];
  40. }
  41. - (id)bk_dynamicDelegateForProtocol:(Protocol *)protocol
  42. {
  43. Class class = [A2DynamicDelegate class];
  44. NSString *protocolName = NSStringFromProtocol(protocol);
  45. if ([protocolName hasSuffix:@"Delegate"]) {
  46. class = a2_dynamicDelegateClass([self class], @"Delegate");
  47. } else if ([protocolName hasSuffix:@"DataSource"]) {
  48. class = a2_dynamicDelegateClass([self class], @"DataSource");
  49. }
  50. return [self bk_dynamicDelegateWithClass:class forProtocol:protocol];
  51. }
  52. - (id)bk_dynamicDelegateWithClass:(Class)cls forProtocol:(Protocol *)protocol
  53. {
  54. /**
  55. * Storing the dynamic delegate as an associated object of the delegating
  56. * object not only allows us to later retrieve the delegate, but it also
  57. * creates a strong relationship to the delegate. Since delegates are weak
  58. * references on the part of the delegating object, a dynamic delegate
  59. * would be deallocated immediately after its declaring scope ends.
  60. * Therefore, this strong relationship is required to ensure that the
  61. * delegate's lifetime is at least as long as that of the delegating object.
  62. **/
  63. __block A2DynamicDelegate *dynamicDelegate;
  64. dispatch_sync(a2_backgroundQueue(), ^{
  65. dynamicDelegate = objc_getAssociatedObject(self, (__bridge const void *)protocol);
  66. if (!dynamicDelegate)
  67. {
  68. dynamicDelegate = [[cls alloc] initWithProtocol:protocol];
  69. objc_setAssociatedObject(self, (__bridge const void *)protocol, dynamicDelegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  70. }
  71. });
  72. return dynamicDelegate;
  73. }
  74. @end