块而不是performSelector:withObject:afterDelay:

Rit*_*its 87 iphone cocoa-touch objective-c ios objective-c-blocks

我经常想在将来几微秒内执行一些代码.现在,我这样解决:

- (void)someMethod
{
    // some code
}
Run Code Online (Sandbox Code Playgroud)

还有这个:

[self performSelector:@selector(someMethod) withObject:nil afterDelay:0.1];
Run Code Online (Sandbox Code Playgroud)

它有效,但我每次都要创建一个新方法.是否可以使用块而不是这个?基本上我正在寻找一种方法,如:

[self performBlock:^{
    // some code
} afterDelay:0.1];
Run Code Online (Sandbox Code Playgroud)

那对我来说真的很有用.

Joh*_*eek 106

没有内置的方法可以做到这一点,但通过类别添加并不是太糟糕:

@implementation NSObject (PerformBlockAfterDelay)

- (void)performBlock:(void (^)(void))block 
          afterDelay:(NSTimeInterval)delay 
{
    block = [[block copy] autorelease];
    [self performSelector:@selector(fireBlockAfterDelay:) 
               withObject:block 
               afterDelay:delay];
}

- (void)fireBlockAfterDelay:(void (^)(void))block {
    block();
}

@end
Run Code Online (Sandbox Code Playgroud)

感谢迈克灰为基本实现.

  • @Kevin:仅为方便起见.如果你想直接使用它而不是直接使用GDC并调用`dispatch_after(dispatch_time_t,dispatch_queue_t,dispatch_block_t)`函数是正确的方法. (19认同)
  • 我应该注意到`[self performBlock:^ {/*some block*/} afterDelay:0.1]`的想法没有多大意义.为什么这根本附在一个物体上?"自我"在发射区块中扮演什么角色?你最好写一个C函数`RunBlockAfterDelay(void(^ block)(void),NSTimeInterval delay)`,尽管这需要创建一个临时对象,其唯一的工作是实现`-fireBlockAfterDelay:`. (8认同)
  • 我完全同意Kevin的评论:在`NSObject`上添加一个类别,基本上用调用方法的方法替换对`dispatch_after`的调用(至少在Lion上的iOS模拟器中)_seems_通过调用`dispatch_after`来实现在我看来,为了什么都不做,只是调用一个调用无参数块的方法,有点过于间接和包装.特别是因为Xcode 4甚至附带了一个开箱即用的简单`dispatch_after`的代码片段...... (6认同)
  • 注意:您应该始终为类别方法选择一个前缀,例如`mon_performBlock:afterDelay:`.这将减少您的方法与其他实现冲突的可能性.最常见的例子:apple决定添加这个方法 - 哎呀,你的方法不会替换已经加载的东西,如果它确实...它会更加痛苦. (5认同)
  • Apple工程团队不允许在更新中破坏关键应用程序.你真的认为OSX可能会随着每一份Photoshop的发布而崩溃吗? (4认同)
  • 不,最糟糕的情况是Cocoa团队必须选择一个不太理想的名称,因为有人取了好名字.或者,如果您的应用程序不足以评估该处理,请在行为更改时让它在新操作系统上崩溃. (3认同)
  • @Catfish_Man直到现在才看到你的回复.这完全是荒谬的; 他们不会根据应用程序的变量命名决定,无论它有多重要.如果API发生变化,您的应用可能会崩溃:大不了!更新它.无论如何,您总是必须为新的OS版本更新您的应用程序.这里没有问题. (2认同)

Nic*_*ore 41

这是一个基于GCD的简单技术,我正在使用:

void RunBlockAfterDelay(NSTimeInterval delay, void (^block)(void))
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC*delay),
      dispatch_get_current_queue(), block);
}
Run Code Online (Sandbox Code Playgroud)

我不是GCD专家,我对这个解决方案的评论感兴趣.

  • 我唯一要补充的是,相反地使用参数可以使调用更加直观,因为它们的顺序将遵循函数名称中名词的顺序. (3认同)
  • @danyowdee正如其他回复中提到的那样,这是错误的.根据http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html"方块应始终是方法的最后一个参数" (3认同)

nin*_*eer 22

另一种方式(可能是出于多种原因最糟糕的方式):

[UIView animateWithDuration:0.0 delay:5.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
} completion:^(BOOL finished) {
    //do stuff here
}];
Run Code Online (Sandbox Code Playgroud)


Gre*_*mbs 16

如果你特别需要更长的延迟,上面的解决方案就可以了.我用@ nick的方法取得了巨大的成功.

但是,如果您只想在主循环的下一次迭代期间运行块,则可以使用以下内容进一步修剪它:

[[NSOperationQueue mainQueue] addOperationWithBlock:aBlock];
Run Code Online (Sandbox Code Playgroud)

这类似于使用performSelector:afterDelay为0.0f

  • +1,但现有performSelector的一个优点是能够取消.这不是零延迟的问题,但即便如此,它也可能有助于解决潜在的比赛. (4认同)

Des*_*vic 11

我使用了类似的代码:

double delayInSeconds = 0.2f;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
      //whatever you wanted to do here...  
    });
Run Code Online (Sandbox Code Playgroud)