【问题标题】:How to pass arguments when calling function with timer in objective c在目标c中使用计时器调用函数时如何传递参数
【发布时间】:2009-10-06 18:06:54
【问题描述】:
-(void)setX:(int)x andY:(int)y andObject:(Sprite*)obj
{
    [obj setPosition:CGPointMake(x,y)];
}

现在,我想调用上面的方法,使用下面的计时器。

[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector() userInfo:nil repeats:NO];

在这里设置什么?

如何传递参数? (据我所知 - 选择器仅指定要调用的方法)

【问题讨论】:

标签: iphone objective-c nstimer


【解决方案1】:

您需要改用+[NSTimer scheduledTimerWithTimeInterval:invocation:repeats:]。默认情况下,用于触发计时器的选择器采用一个参数。如果您需要除此之外的其他内容,则必须创建一个 NSInvocation 对象,计时器将使用该对象。

【讨论】:

    【解决方案2】:

    如果您想使用一组相当复杂的参数来调用该方法,我建议您将参数捕获到包含配置的东西中,并且可以根据该配置执行任何需要执行的操作...

    类似这样的界面的东西:

    PositionSetter.h:

    @interface  PositionSetter : NSObject
    {
        NSInteger x;
        NSInteger y;
        Sprite *target;
    }
    
    + positionSetterWithX: (NSInteger) xPos y: (NSInteger) yPos sprite: (Sprite *) aSprite; 
    
    - (void) applyPosition;
    @end
    

    PositionSetter.m:

    @interface PositionSetter()
    @property(readwrite, nonatomic) NSInteger x;
    @property(readwrite, nonatomic) NSInteger y;
    @property(readwrite, nonatomic, retain) Sprite *target;
    @end
    
    @implementation PositionSetter
    @synthesize x, y, target;
    
    + positionSetterWithX: (NSInteger) xPos y: (NSInteger) yPos sprite: (Sprite *) aSprite; 
    {
        PositionSetter *positionSetter = [PositionSetter new];
        positionSetter.x = xPos;
        positionSetter.y = yPos;
        positionSetter.target = aSprite;
        return [positionSetter autorelease];
    }
    
    - (void) applyPosition;
    {
        [self.target setPosition:CGPointMake(self.x,self.y)];
    }
    @end
    

    用法很简单:

    positionSetter = [PositionSetter positionSetterWithX: 42 y: 21 sprite: mySprite];
    [positionSetter performSelector: @selector(applyPosition) withObject: nil afterDelay: 1.0];
    

    虽然代码多一点,但最终的实现将足够快——可能比 NSInvocation 快,但考虑到这会导致绘图,速度足够快以至于无关紧要——而且更灵活。我可以很容易地看到将上述内容重构为驱动,例如 CoreAnimation。

    【讨论】:

    • 您不觉得吗,先生,解决方案有点复杂。不是他们的小伎俩吗?
    • 最简单的方法,请参阅我的问题下方的评论 - 或查看我的答案。
    • 简单并不总是最好的。您的答案当然有效,但我不想维护或重构该代码。同时,快速破解通常比优雅的代码更有效地支付账单。 ;)
    • @bbum - 完全正确。如果我确实按照您的方式实施,那么它是完全安全且最好的。我的回答给出了简短的方法,没有任何安全性(数据类型不受管理)。其实我误解了“hack”
    【解决方案3】:

    Matt Ball的回答中复制:

        - (void)startMyTimer {
            /* ... Some stuff ... */
            NSDictionary *userDict;
            userDict = [NSDictionary dictionaryWithObjectsAndKeys:someValue,
                                                                  @"value1",
                                                                  someOtherValue,
                                                                  @"value2", nil];
    
            [NSTimer scheduledTimerWithTimeInterval:0.1
                                             target:self
                                           selector:@selector(callMyMethod:)
                                           userInfo:userDict
                                            repeats:YES];
    }
        - (void)callMyMethod:(NSTimer *)theTimer {
            NSString *value1 = [[theTimer userInfo] objectForKey:@"value1"];
            NSString *value2 = [[theTimer userInfo] objectForKey:@"value2"];
            [self myMethod:value1 setValue2:value2];
        }
    

    【讨论】:

    • 赞成;一个黑客......当然......但有时你只需要一个黑客。 拍手
    • 我没说错。我投了赞成票。工作正常... hack 指的是它正在进行装箱/拆箱的事实,因此,所有类型的安全性都丢失了,这也增加了脆弱性。没有错;只是需要注意的事情。
    • 我应该使用“hacque”这个词来表示它是一流的 hacque。 :)
    【解决方案4】:

    如果您使用目标动作计时器,则不能让计时器直接调用任意方法。计时器的动作必须有一个非常具体的签名。您可以在 userinfo 字典中传递其他数据,并让计时器的操作调用您最终想要的方法,或者您可以使用 Dave 所说的调用表单。就我个人而言,我通常使用前者,因为我发现 NSInvocations 很烦人,而且设置一个实际上可能需要更多的代码,而不仅仅是编写一个中间方法。

    【讨论】:

      【解决方案5】:

      您可以将 NSDictionary* 或其他对象作为 userInfo 传递,并将参数放入其中。

      【讨论】:

      • 好的。但是如何通过 NSDictionary?
      【解决方案6】:

      作为 NSTimer 的替代方案,在 iOS 4.0+ 和 10.6+ 上,您可以使用 Grand Central Dispatch 和调度源使用块来执行此操作。 Apple 在其Concurrency Programming Guide 中有以下代码:

      dispatch_source_t CreateDispatchTimer(uint64_t interval, uint64_t leeway, dispatch_queue_t queue, dispatch_block_t block)
      {
          dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
          if (timer)
          {
              dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval, leeway);
              dispatch_source_set_event_handler(timer, block);
              dispatch_resume(timer);
          }
          return timer;
      }
      

      然后您可以使用如下代码设置一秒定时器事件:

      dispatch_source_t newTimer = CreateDispatchTimer(1ull * NSEC_PER_SEC, (1ull * NSEC_PER_SEC) / 10, dispatch_get_main_queue(), ^{
          [self setX:someValue andY:otherValue andObject:obj];
      });
      

      只要您在完成后存储并释放计时器。这甚至可以让您通过使用并发队列而不是上面使用的主队列来触发计时器以在后台线程上执行项目。

      这可以避免装箱和拆箱参数的需要。

      【讨论】:

        【解决方案7】:

        使用这些参数创建字典并使用计时器用户信息传递该字典。这将解决您的问题

        【讨论】:

          猜你喜欢
          • 2023-03-31
          • 2020-08-19
          • 1970-01-01
          • 2010-11-21
          • 2019-09-07
          • 1970-01-01
          • 2012-06-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多