更新:
通过一些关键建议以及来回与乔治,我提出了两种不同的方法来实现我想要的CodeRunner并将其发布在Github的gist网站上: Objective-C AOP gist
代码粗糙,因为它是一个新概念,我刚刚在凌晨1:30结束。它肯定有效,并有一些细节,如自动添加所有非初始化器,getter或setter的方法。 [END UPDATE]
有几次(但肯定不是经常)我遇到过一种情况,如果我可以为类中的每个方法调用一个上下文相关的代码片段,那么我的代码会有点干扰。使用Objective-C运行时完全没问题,我也接受C或C ++解决方案。
而不是:
- (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将包含更多代码,并且类中会有更多方法。
P.S。,我对DRY非常着迷。除了散文和表现的清晰度之外,它还是我长期评估代码质量的关键因素。对于每种新方法,我都可以避免重复自己,这种好处是指数级的,因为我在许多项目共享的可重用类中尽可能多地中断代码。
答案 0 :(得分:5)
对于问题中的特定用例,可以提供一个处理程序来替换原始实现函数,并使用类似this approach之类的函数来调用处理程序之前/之后以及原始函数。通常情况下,方法实现修补不起作用,因为必须为每个截获的方法签名提供处理程序/拦截方法。
更通用的(即对于除变量参数函数之外的所有内容)将会处理-forwardInvocation:
。这里的问题是我们必须首先调用该方法。由于我们无法删除ObjC2中的方法,因此无法完成。
然而,可以使用代理实现forwardInvocation:
并调用我们的前/后处理程序。
@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];
...将打印:
forwardInvocation:someFunction
在someFunction之前 一些称为
的函数 在someFunction之后
可以在this gist中找到可运行的样本。