AOP
可以通过预编译方式和 运行期动态代理 实现在不修改源代码的情况下给程序动态统一添加功能的一种技术.
用于:日志记录,性能统计,安全控制,事务处理,异常处理等等。
将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
添加属性
在头文件中声明属性,也就是 xxxController 代理方法的对应 block 属性,以UIImageViewController为例。
@property (nonatomic,copy) void(^bk_didFinishPickingMediaBlock)(UIImagePickerController *,NSDictionary *);
@property (nonatomic,copy) void(^bk_didCancelBlock)(UIImagePickerController *);
并且声明为动态生成这两个属性的存取方法。
@dynamic bk_didFinishPickingMediaBlock;
@dynamic bk_didCancelBlock;
注册动态代理对象
获取protocol对象
在NSObject的block代理扩展:NSObject+A2BlockDelegate
里,调用:a2_delegateProtocol(self)
。传递self以及字符串@“Delegate”,拼装出如UIImagePickerControllerDelegate
。根据名称获取Protocol对象。
Protocol *a2_delegateProtocol(Class cls)
{
return a2_classProtocol(cls, @"Delegate", @"delegate");
}
通过传入具体的Class。用反射机制得到类的名称,再通过传入的Delegate
拼接出改类所对应的代理类名称。例如UIImagePickerController
所对应的代理为UIImagePickerControllerDelegate
。
那么这里的_cls
即为UIImagePickerController
,suffix
为@"Delegate"
。
Protocol * objc_getProtocol ( const char *name );
根据名字,返回指定的协议。
如果仅仅是声明了一个协议,而未在任何类中实现这个协议,则该函数返回的是nil。
开始注册
这一步,会替换UIImagePickerController原来的setDelegate和delegate方法,并且用dynamicDelegate来替换原来的delegate对象。
+ (void)bk_registerDynamicDelegateNamed:(NSString *)delegateName forProtocol:(Protocol *)protocol
{
NSMapTable *propertyMap = [self bk_delegateInfoByProtocol:YES];
A2BlockDelegateInfo *infoAsPtr = (__bridge void *)[propertyMap objectForKey:protocol];
if (infoAsPtr != NULL) { return; }
const char *name = delegateName.UTF8String;
objc_property_t property = class_getProperty(self, name);
SEL setter = setterForProperty(property, name);
SEL a2_setter = prefixedSelector(setter);
SEL getter = getterForProperty(property, name);
A2BlockDelegateInfo info = {
setter, a2_setter, getter
};
[propertyMap setObject:(__bridge id)&info forKey:protocol];
infoAsPtr = (__bridge void *)[propertyMap objectForKey:protocol];
IMP setterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject, id delegate) {
A2DynamicDelegate *dynamicDelegate = getDynamicDelegate(delegatingObject, protocol, infoAsPtr, YES);
if ([delegate isEqual:dynamicDelegate]) {
delegate = nil;
}
dynamicDelegate.realDelegate = delegate;
});
if (!swizzleWithIMP(self, setter, a2_setter, setterImplementation, "v@:@", YES)) {
bzero(infoAsPtr, sizeof(A2BlockDelegateInfo));
return;
}
if (![self instancesRespondToSelector:getter]) {
IMP getterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject) {
return [delegatingObject bk_dynamicDelegateForProtocol:a2_protocolForDelegatingObject(delegatingObject, protocol)];
});
addMethodWithIMP(self, getter, NULL, getterImplementation, "@@:", NO);
}
}
将protocol对象与delegate的setter和getter方法关联。
// 创建一个映射关系容器。
NSMapTable *propertyMap = [self bk_delegateInfoByProtocol:YES];
A2BlockDelegateInfo *infoAsPtr = (__bridge void *)[propertyMap objectForKey:protocol];
if (infoAsPtr != NULL) { return; }
// 根据delegate这个property得到对应的setter和getter方法。
const char *name = delegateName.UTF8String;
objc_property_t property = class_getProperty(self, name);
SEL setter = setterForProperty(property, name);
SEL a2_setter = prefixedSelector(setter);
SEL getter = getterForProperty(property, name);
A2BlockDelegateInfo info = {
setter, a2_setter, getter
};
[propertyMap setObject:(__bridge id)&info forKey:protocol];
infoAsPtr = (__bridge void *)[propertyMap objectForKey:protocol];
将原来delegate的setter方法替换为block实现。
使用block实现delegate的setter方法。当有如下调用时:controller.delegate = self;就会触发这个函数指针。
其中,delegatingObject为pickerController,delegate就是被代理的对象:xxController。
IMP setterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject, id delegate) {
A2DynamicDelegate *dynamicDelegate = getDynamicDelegate(delegatingObject, protocol, infoAsPtr, YES);
if ([delegate isEqual:dynamicDelegate]) {
delegate = nil;
}
dynamicDelegate.realDelegate = delegate;
});
if (!swizzleWithIMP(self, setter, a2_setter, setterImplementation, "v@:@", YES)) {
bzero(infoAsPtr, sizeof(A2BlockDelegateInfo));
return;
}
使用A2DynamicDelegate对象替换原来的delegate。
通过UIImagePickerController及UIImagePickerControllerDelegate protocol,构造一个继承自NSProxy的dynamicDelegate。
A2DynamicDelegate *dynamicDelegate = getDynamicDelegate(delegatingObject, protocol, infoAsPtr, YES);
if ([delegate isEqual:dynamicDelegate]) {
delegate = nil;
}
dynamicDelegate.realDelegate = delegate;
- 1、构造:
A2DynamicUIImagePickerControllerDelegate
。继承自A2DynamicDelegate
。并且实现:UIImagePickerControllerDelegate
代理方法。 - 2、得到
A2DynamicUIImagePickerControllerDelegate
代理对象,这个对象通过类关联的方式设置为UIImagePickerController
的属性。dynamicDelegate
。 - 3、前一步,通过消息转发获取controller的delegate,如果已经设置过了delegate为dynamicDelegate,那么直接返回。
- 4、如果还没有设置,那么通过消息转发机制将UIImagePickerController的delegate设置为dynamicDelegate。(动态改变原来的代理对象为A2DynamicUIImagePickerControllerDelegate对象。这样后续的代理方法调用,都会走到该对象实现的UIImagePickerControllerDelegate代理方法中)。
- 5、保存原来的真正delegate。这样,如果原来实现了UIImagePickerControllerDelegate的方法,则通过这个realDelegate继续调用原来的代理方法。
static inline A2DynamicDelegate *getDynamicDelegate(NSObject *delegatingObject, Protocol *protocol, const A2BlockDelegateInfo *info, BOOL ensuring) {
A2DynamicDelegate *dynamicDelegate = [delegatingObject bk_dynamicDelegateForProtocol:a2_protocolForDelegatingObject(delegatingObject, protocol)];
if (!info || !info->setter || !info->getter) {
return dynamicDelegate;
}
if (!info->a2_setter && !info->setter) { return dynamicDelegate; }
id (*getterDispatch)(id, SEL) = (id (*)(id, SEL)) objc_msgSend;
id originalDelegate = getterDispatch(delegatingObject, info->getter);
if (bk_object_isKindOfClass(originalDelegate, A2DynamicDelegate.class)) { return dynamicDelegate; }
void (*setterDispatch)(id, SEL, id) = (void (*)(id, SEL, id)) objc_msgSend;
setterDispatch(delegatingObject, info->a2_setter ?: info->setter, dynamicDelegate);
return dynamicDelegate;
}
将原来delegate的getter方法替换为block实现。
这里会先判断有没有实现getter方法。
if (![self instancesRespondToSelector:getter]) {
IMP getterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject) {
return [delegatingObject bk_dynamicDelegateForProtocol:a2_protocolForDelegatingObject(delegatingObject, protocol)];
});
addMethodWithIMP(self, getter, NULL, getterImplementation, "@@:", NO);
}
在dynamicDelegate中生成一个映射
key为原始的代理方法的selector,value为bi_xxxx所传递的block参数。并且生成A2BlockInvocation来保存这个block。
[self bk_linkDelegateMethods:@{ @"bk_didFinishPickingMediaBlock": @"imagePickerController:didFinishPickingMediaWithInfo:",
@"bk_didCancelBlock": @"imagePickerControllerDidCancel:" }];
dynamicDelegate中触发了原始的代理方法,那么根据原始方法的selector从dynamicDelegate的映射表中取出invocation。并且执行invocation保存的block。
达到将代理方法的执行放到block中执行。
+ (void)bk_linkProtocol:(Protocol *)protocol methods:(NSDictionary *)dictionary
{
[dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *selectorName, BOOL *stop) {
const char *name = propertyName.UTF8String;
objc_property_t property = class_getProperty(self, name);
NSCAssert(property, @"Property \"%@\" does not exist on class %s", propertyName, class_getName(self));
char *dynamic = property_copyAttributeValue(property, "D");
NSCAssert2(dynamic, @"Property \"%@\" on class %s must be backed with \"@dynamic\"", propertyName, class_getName(self));
free(dynamic);
char *copy = property_copyAttributeValue(property, "C");
NSCAssert2(copy, @"Property \"%@\" on class %s must be defined with the \"copy\" attribute", propertyName, class_getName(self));
free(copy);
SEL selector = NSSelectorFromString(selectorName);
SEL getter = getterForProperty(property, name);
SEL setter = setterForProperty(property, name);
if (class_respondsToSelector(self, setter) || class_respondsToSelector(self, getter)) { return; }
const A2BlockDelegateInfo *info = [self bk_delegateInfoForProtocol:protocol];
IMP getterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject) {
A2DynamicDelegate *delegate = getDynamicDelegate(delegatingObject, protocol, info, NO);
return [delegate blockImplementationForMethod:selector];
});
if (!class_addMethod(self, getter, getterImplementation, "@@:")) {
NSCAssert(NO, @"Could not implement getter for \"%@\" property.", propertyName);
}
IMP setterImplementation = imp_implementationWithBlock(^(NSObject *delegatingObject, id block) {
A2DynamicDelegate *delegate = getDynamicDelegate(delegatingObject, protocol, info, YES);
[delegate implementMethod:selector withBlock:block];
});
if (!class_addMethod(self, setter, setterImplementation, "v@:@")) {
NSCAssert(NO, @"Could not implement setter for \"%@\" property.", propertyName);
}
}];
}
创建一个对象->对象的映射关系
NSDictionary 的局限性
NSDictionary 提供了 key -> object 的映射。从本质上讲,NSDictionary 中存储的 object 位置是由 key 来索引的。
由于对象存储在特定位置,NSDictionary 中要求 key 的值不能改变(否则 object 的位置会错误)。为了保证这一点,NSDictionary 会始终复制 key 到自己私有空间。
这个 key 的复制行为也是 NSDictionary 如何工作的基础,但这也有一个限制:你只能使用 OC 对象作为 NSDictionary 的 key,并且必须支持 NSCopying 协议。此外,key 应该是小且高效的,以至于复制的时候不会对 CPU 和内存造成负担。
这意味着,NSDictionary 中真的只适合将值类型的对象作为 key(如简短字符串和数字)。并不适合自己的模型类来做对象到对象的映射。
NSMapTable(顾名思义)更适合于一般来说的映射概念。这取决于它的设计方式,NSMapTable 可以处理的 key -> obj 式映射如 NSDictionary,但它也可以处理 obj -> obj 的映射。
其他
下面这个函数用于构造一个protocol所持有的所有property。
+ (NSMapTable *)bk_delegateInfoByProtocol:(BOOL)createIfNeeded
{
NSMapTable *delegateInfo = objc_getAssociatedObject(self, _cmd);
if (delegateInfo || !createIfNeeded) { return delegateInfo; }
NSPointerFunctions *protocols = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsOpaqueMemory|NSPointerFunctionsObjectPointerPersonality];
NSPointerFunctions *infoStruct = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsMallocMemory|NSPointerFunctionsStructPersonality|NSPointerFunctionsCopyIn];
infoStruct.sizeFunction = A2BlockDelegateInfoSize;
infoStruct.descriptionFunction = A2BlockDelegateInfoDescribe;
delegateInfo = [[NSMapTable alloc] initWithKeyPointerFunctions:protocols valuePointerFunctions:infoStruct capacity:0];
objc_setAssociatedObject(self, _cmd, delegateInfo, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return delegateInfo;
}
// 获取指定的属性
objc_property_t class_getProperty ( Class cls, const char *name );
下面获取属性的setter方法。
static SEL setterForProperty(objc_property_t property, const char *name)
{
if (property) {
char *setterName = property_copyAttributeValue(property, "S");
if (setterName) {
SEL setter = sel_getUid(setterName);
free(setterName);
if (setter) return setter;
}
}
const char *propertyName = property ? property_getName(property) : name;
return selectorWithPattern("set", propertyName, ":");
}
(A2BlockDelegateInfo) info = (setter = "setDelegate:", a2_setter = "a2_SetDelegate:", getter = "delegate")
获取属性中指定的特性
char * property_copyAttributeValue ( objc_property_t property, const char *attributeName );
新加的属性必须是以@dynamic来修饰,不通过系统自动生成setter和getter方法。
char *dynamic = property_copyAttributeValue(property, "D");
网友评论