12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 |
- //
- // NSObject+A2DynamicDelegate.m
- // BlocksKit
- //
- #import <objc/runtime.h>
- #import "NSObject+A2DynamicDelegate.h"
- extern Protocol *a2_dataSourceProtocol(Class cls);
- extern Protocol *a2_delegateProtocol(Class cls);
- static Class a2_dynamicDelegateClass(Class cls, NSString *suffix)
- {
- while (cls) {
- NSString *className = [NSString stringWithFormat:@"A2Dynamic%@%@", NSStringFromClass(cls), suffix];
- Class ddClass = NSClassFromString(className);
- if (ddClass) return ddClass;
- cls = class_getSuperclass(cls);
- }
- return [A2DynamicDelegate class];
- }
- static dispatch_queue_t a2_backgroundQueue(void)
- {
- static dispatch_once_t onceToken;
- static dispatch_queue_t backgroundQueue = nil;
- dispatch_once(&onceToken, ^{
- backgroundQueue = dispatch_queue_create("BlocksKit.DynamicDelegate.Queue", DISPATCH_QUEUE_SERIAL);
- });
- return backgroundQueue;
- }
- @implementation NSObject (A2DynamicDelegate)
- - (id)bk_dynamicDataSource
- {
- Protocol *protocol = a2_dataSourceProtocol([self class]);
- Class class = a2_dynamicDelegateClass([self class], @"DataSource");
- return [self bk_dynamicDelegateWithClass:class forProtocol:protocol];
- }
- - (id)bk_dynamicDelegate
- {
- Protocol *protocol = a2_delegateProtocol([self class]);
- Class class = a2_dynamicDelegateClass([self class], @"Delegate");
- return [self bk_dynamicDelegateWithClass:class forProtocol:protocol];
- }
- - (id)bk_dynamicDelegateForProtocol:(Protocol *)protocol
- {
- Class class = [A2DynamicDelegate class];
- NSString *protocolName = NSStringFromProtocol(protocol);
- if ([protocolName hasSuffix:@"Delegate"]) {
- class = a2_dynamicDelegateClass([self class], @"Delegate");
- } else if ([protocolName hasSuffix:@"DataSource"]) {
- class = a2_dynamicDelegateClass([self class], @"DataSource");
- }
- return [self bk_dynamicDelegateWithClass:class forProtocol:protocol];
- }
- - (id)bk_dynamicDelegateWithClass:(Class)cls forProtocol:(Protocol *)protocol
- {
- /**
- * Storing the dynamic delegate as an associated object of the delegating
- * object not only allows us to later retrieve the delegate, but it also
- * creates a strong relationship to the delegate. Since delegates are weak
- * references on the part of the delegating object, a dynamic delegate
- * would be deallocated immediately after its declaring scope ends.
- * Therefore, this strong relationship is required to ensure that the
- * delegate's lifetime is at least as long as that of the delegating object.
- **/
- __block A2DynamicDelegate *dynamicDelegate;
- dispatch_sync(a2_backgroundQueue(), ^{
- dynamicDelegate = objc_getAssociatedObject(self, (__bridge const void *)protocol);
- if (!dynamicDelegate)
- {
- dynamicDelegate = [[cls alloc] initWithProtocol:protocol];
- objc_setAssociatedObject(self, (__bridge const void *)protocol, dynamicDelegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- });
- return dynamicDelegate;
- }
- @end
|