90 lines
2.8 KiB
Mathematica
90 lines
2.8 KiB
Mathematica
|
|
//
|
||
|
|
// NSObject+A2DynamicDelegate.m
|
||
|
|
// BlocksKit
|
||
|
|
//
|
||
|
|
|
||
|
|
#import "NSObject+A2DynamicDelegate.h"
|
||
|
|
@import ObjectiveC.runtime;
|
||
|
|
#import "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
|