C++ Objective-C中的AOP:将上下文感知代码注入到每个方法中,同时保持干燥

C++ Objective-C中的AOP:将上下文感知代码注入到每个方法中,同时保持干燥,c++,objective-c,c,aop,dry,C++,Objective C,C,Aop,Dry,更新: 我提出了一些关键建议,并与George进行了反复讨论,提出了两种不同的方法来实现我在CodeRunner中想要的目标,并将其发布在Github的gist网站上: 代码很粗糙,因为这是一个新概念,我刚在凌晨1:30完成。但它确实可以工作,并且有一些细节,比如自动添加所有不是初始值设定项、getter或setter的方法。[结束更新] 有好几次(但肯定不是经常)我遇到这样一种情况:如果我可以为类中的每个方法调用上下文敏感的代码片段,我的代码就会有点枯燥。使用Objto-C运行时是完全好的,

更新:

我提出了一些关键建议,并与George进行了反复讨论,提出了两种不同的方法来实现我在CodeRunner中想要的目标,并将其发布在Github的gist网站上:

代码很粗糙,因为这是一个新概念,我刚在凌晨1:30完成。但它确实可以工作,并且有一些细节,比如自动添加所有不是初始值设定项、getter或setter的方法。[结束更新]

有好几次(但肯定不是经常)我遇到这样一种情况:如果我可以为类中的每个方法调用上下文敏感的代码片段,我的代码就会有点枯燥。使用Objto-C运行时是完全好的,我也接受C或C++解决方案。p> 而不是:

- (void)methodName1
{
   self->selector = _cmd;
   NSLog(@"This method is named: %@",_cmd);
   //more code
}

- (void)methodName2
{
   self->selector = _cmd;
   NSLog(@"This method is named: %@",_cmd);
   //more code
}
这样做,结果是一样的:

+ (void)AOPMethod
{
   self->selector = _cmd;
   NSLog(@"This method is named: %@",_cmd);
}

- (void)methodName1
{
   //more code
}

- (void)methodName2
{
   //more code
}
在实际应用程序中,AOPMethod将包含更多的代码,并且类中会有更多的方法


顺便说一句,我对干货很着迷。除了清晰的文字和性能,它也是我如何长期评估代码质量的一个关键组成部分。对于每一种我可以避免重复自己的新方法,其好处是指数级的,因为我在许多项目中共享的可重用类中尽可能多地分离代码

对于问题中的特定用例,可以提供一个处理程序来替换原始实现函数和处理程序之前/之后的调用,以及使用类似的方式替换原始函数。但是,一般来说,方法实现补丁不起作用,因为必须为每个被拦截的方法签名提供处理程序/拦截方法

处理
-forwardInvocation:
更通用(即除变量参数函数外的所有函数)。但这里的问题是,我们必须首先调用该方法。由于我们无法删除ObjC2中的方法,因此无法在适当的位置执行

但是,可以使用代理实现
forwardInvocation:
并调用我们的before/after处理程序

@interface AspectProxy : NSProxy {
    id target_;
}
- (id)initWithTarget:(id)target;
@end

@implementation AspectProxy
- (id)initWithTarget:(id)target {
    target_ = [target retain];
    return self;
}
- (void)dealloc {
    [target_ release];
    [super dealloc];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return [target_ methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)inv {
    SEL sel = [inv selector];
    NSLog(@"forwardInvocation for: %@", NSStringFromSelector(sel));
    if (sel == @selector(aspectBefore:) || sel == @selector(aspectAfter:)) {
        return;
    }
    if ([target_ respondsToSelector:@selector(aspectBefore:)]) {
        [target_ performSelector:@selector(aspectBefore:) withObject:inv];
    }
    [inv invokeWithTarget:target_];
    if ([target_ respondsToSelector:@selector(aspectAfter:)]) {
        [target_ performSelector:@selector(aspectAfter:) withObject:inv];
    }
}
@end
由于我们不需要从
init
方法返回实际实例,这甚至可以透明地完成:

@interface Test : NSObject
- (void)someFunction;
@end

@implementation Test
- (id)init {
    if (self = [super init]) {
        return [[AspectProxy alloc] initWithTarget:[self autorelease]];
    }
    return self;
}
- (void)aspectBefore:(NSInvocation *)inv {
    NSLog(@"before %@", NSStringFromSelector([inv selector]));
}
- (void)aspectAfter:(NSInvocation *)inv {
    NSLog(@"after %@", NSStringFromSelector([inv selector]));
}
- (void)someFunction {
    NSLog(@"some function called");
}
@end
现在输入以下代码:

Test *x = [[[Test alloc] init] autorelease];
[x someFunction];
。。。将打印:

对:someFunction的转发调用
在某些函数之前
一些函数被称为
在某些功能之后


可以在中找到一个可运行的示例。

您只是在寻找进入/退出截获吗?@GeorgFritzsche谢谢您的提问。如果一个方法截获是平台无关的,并且允许在类的每个方法中使用方法级上下文敏感数据,例如_cmd,并且不存在代码重复,那么是。换言之,如果您可以使用所提到的一种基本语言(而不是所有平台上都提供的框架)共享一种技术,这将允许后一个示例的某些版本具有与前一个相同的结果,那么这就是一个答案。再次感谢。对于您的特定用例(具有相同签名的所有方法),可以扩展以修补方法列表中所有合适的方法。“目前,我想不出一个更优雅、更通用的解决方案,只有低效的解决方案。@GeorgFritzsche我认为您提供的链接中建议的技术的某些版本可能会起作用。”。不过,我已经运行了一些测试,但还没有找到一种方法能够为每个方法进行一个或两个调用。最后,我认为使用Nproxy可能会更干净。我链接的内容始终需要存在一个拦截器方法(即调用“before”和“after”处理程序的拦截器方法),并带有所需方法的方法签名。我能想到的唯一更通用的解决方案是使用
NSInvocation
s处理它,但要获得
forwardInvocation:
调用,您需要删除原始方法(在ObjC 2中不再可能)。我认为无需求助于黑客,替身是下一个最好的选择。这是一个非常酷的解决方案。我曾经做过类似的事情来创建一个委托代理,它可以避免为可选方法编写
if([delegate respondsToSelector:]){}
。很好!最后,我确实使用了forwardInvocation和NSProxy作为选项之一(我在发布问题后不久就知道了这一点),但我更痴迷于使用运行时,所以我也想出了一种方法。这两种方法在我问题的要点中都有联系。我喜欢你的Nproxy版本有多干净。