如何在具有多个参数的方法上使用performSelector:withObject:afterDelay:?

5

假设我有一个具有以下签名的方法:

 -(void)plotPoly:(Polygon *)poly WithColor:(UIColor *)color AndFill:(BOOL)filled;

我应该如何将 UIColorBOOL 以及 Polygon 放进去呢?

我应该将它们包装在 NSArray 中,在调用的方法内部取出来吗?这意味着我需要改变方法的签名,对吗?

有更简洁的方法吗?


我们可以从UIColor推断出你正在使用iPhone吗? - nall
我是。那有什么区别吗? - willc2
1
是的 - 如果你在雪豹系统上,你可以使用Block来解决这个问题。 - bbum
我使用NSArray策略。在我的一些动画中,回调函数需要六个不同的整数值,以了解如何完成动画并继续执行。(UIView的setAnimationDidStopSelector:也仅允许一个对象同时出现...)将它们打包成NSArray只需要1行代码,在回调函数中解包需要6行代码。代码非常易于理解,但不太可能给任何人留下深刻印象。 - Rob
4个回答

9

虽然还不算优雅,但比改变整个API要好一些的方法是使用NSInvocation:

Polygon *poly;
UIColor *color;
BOOL filled;
// Assume the above variables exist
NSInvocation *inv = [NSInvocation invocationWithMessageSignature:[target messageSignatureForSelector:message]];
[inv setArgument:&poly atIndex:2];
[inv setArgument:&color atIndex:3];
[inv setArgument:&filled atIndex:4];
[inv performSelector:@selector(invokeWithTarget:) withObject:target afterDelay:1];

另一个最好的选择是创建一个包装方法,使用适当的参数(可能作为字典或数组给出),调用您想要的原始方法,该方法匹配执行延迟后所需的签名。请保留HTML标记。

1
为什么参数索引从3开始而不是2(正如文档所示)? - willc2
它从2开始。参数0和1保留用于隐藏的“self”和“_cmd”参数。 - Dave DeLong
很抱歉,我打错了第一个字母,然后就递增修改了接下来的内容。 - Chuck
你是指 'invocationWithMethodSignature' 吗?但是这个方法对我仍然无效。 - Dimitris
这个NSInvocation对象的生命周期会发生什么?如果你在延迟后执行选择器,在当前runloop结束时,这个NSInvocation对象应该会自动释放,因此不会存在于延迟中。如果从一个本身不会长时间存在的对象调用它(即模态视图控制器在下一个运行循环中调用委托),那么你不能将NSInvocation存储在调用对象中。 - Michael Waterfall
@Michael Waterfall:你需要保留这些参数,否则在调用NSInvocation时它们将不可用。我保留了我使用的参数,然后在延迟大于调用时间的情况下释放它们,以免造成泄漏。 - Dov

7

几周前,我回答了一个相似的问题。下面是为这个问题编辑过的答案。

通常情况下,我避免使用NSInvocation来完成这种工作。它往往会成为维护上的头痛,并且特别地,在未来重构时会带来困难。

首先,考虑以下这个方法:

 -(void)plotPoly:(Polygon *)poly WithColor:(UIColor *)color AndFill:(BOOL)filled;

通常会声明为:

 -(void)plotPoly:(Polygon *)aPoly color:(UIColor *)aColor filled:(BOOL)filledFlag;

这个更贴近命名惯例。

现在,我会将参数捕获到一个简单的类中,该类提供一个 -invoke 方法。

接口如下:

PolyPlotter.h:

@interface  PolyPlotter : NSObject
{
    Polygon *poly;
    UIColor *color;
    BOOL filled;
}

+ plotterWithPoly: (Polygon *) aPoly color: (UIColor *) aColor filled: (BOOL) filledFlag; 

- (void) plot;
@end

PolyPlotter.m:

@interface PolyPlotter()
@property Polygon *poly;
@property UIColor *color;
@property BOOL filled;
@end

@implementation PolyPlotter
@synthesize poly, color, filled;

+ plotterWithPoly: (Polygon *) aPoly color: (UIColor *) aColor filled: (BOOL) filledFlag; 
{
    PolyPlotter *polygonPlotter = [PolyPlotter new];
    polygonPlotter.poly = aPoly;
    polygonPlotter.color = aColor;
    polygonPlotter.filled = filledFlag;
    return [polygonPlotter autorelease];
}

- (void) plot;
{
    // ... do your plotting here ...
}
@end

使用方法很简单。只需创建一个PolygonPlotter实例,并告诉它在延迟后或主线程上执行选择器plot即可。

考虑到问题,我怀疑您在绘图时可能需要更多的上下文信息?如果是这样,您可以将该信息作为参数传递给-plot,例如声明该方法:

- (void) plot: (UIView *) aViewToPlotIn;

或者类似这样。

像我说的那样,代码稍微多一些,但比NSInvocation模式更灵活、易于重构。例如,你可以很容易地将PolygonPlotter设置为可存档的。


4

Joe Hewitt的Three20库有一些高级版本的performSelector方法,你可能会发现它们很有用(我只发布了一个片段):

- (id)performSelector:(SEL)selector withObject:(id)p1 withObject:(id)p2 withObject:(id)p3 {
  NSMethodSignature *sig = [self methodSignatureForSelector:selector];
  if (sig) {
    NSInvocation* invo = [NSInvocation invocationWithMethodSignature:sig];
    [invo setTarget:self];
    [invo setSelector:selector];
    [invo setArgument:&p1 atIndex:2];
    [invo setArgument:&p2 atIndex:3];
    [invo setArgument:&p3 atIndex:4];
    [invo invoke];
    if (sig.methodReturnLength) {
      id anObject;
      [invo getReturnValue:&anObject];
      return anObject;
    } else {
      return nil;
    }
  } else {
    return nil;
  }
}

只需将它们添加到NSObject类别中即可。

我该如何实现 afterDelay: 函数? - willc2

0

我认为NSArray是一个合理的解决方案,是的,这意味着改变方法签名以接受一个NSArray*作为其唯一参数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接