如何使用performSelector:withObject:afterDelay:在Cocoa中的原始types?

NSObject方法performSelector:withObject:afterDelay:允许我在一定时间后用对象参数调用对象的方法。 它不能用于具有非对象参数的方法(例如ints,float,structs,non-object pointers等)。

使用非对象参数的方法实现相同的事情最简单的方法是什么? 我知道,对于常规performSelector:withObject:解决scheme是使用NSInvocation (顺便说一下,这是非常复杂的)。 但是我不知道如何处理“延迟”部分。

谢谢,

这是我曾经调用的东西,我不能改变使用NSInvocation:

 SEL theSelector = NSSelectorFromString(@"setOrientation:animated:"); NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature: [MPMoviePlayerController instanceMethodSignatureForSelector:theSelector]]; [anInvocation setSelector:theSelector]; [anInvocation setTarget:theMovie]; UIInterfaceOrientation val = UIInterfaceOrientationPortrait; BOOL anim = NO; [anInvocation setArgument:&val atIndex:2]; [anInvocation setArgument:&anim atIndex:3]; [anInvocation performSelector:@selector(invoke) withObject:nil afterDelay:1]; 

只需在NSNumber中包装float,boolean,int或类似的东西。

对于结构体,我不知道一个方便的解决scheme,但你可以做一个单独的ObjC类拥有这样一个结构。

不要使用这个答案。 我只把它留给历史的目的。 见下面的评论。

如果它是一个BOOL参数有一个简单的技巧。

通过否和否是自我。 nil被转换为NO的BOOL值。 self被转换为YES的BOOL值。

如果它是除BOOL参数以外的其他任何东西的话,这种方法就会崩溃。

假设self是一个UIView。

 //nil will be cast to NO when the selector is performed [self performSelector:@selector(setHidden:) withObject:nil afterDelay:5.0]; //self will be cast to YES when the selector is performed [self performSelector:@selector(setHidden:) withObject:self afterDelay:10.0]; 

也许NSValue ,只要确保你的指针在延迟之后仍然有效(即在堆栈上没有分配对象)。

我知道这是一个古老的问题,但如果你正在构buildiOS SDK 4+,那么你可以使用块做很less的努力,使其更具可读性:

 double delayInSeconds = 2.0; int primitiveValue = 500; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ [self doSomethingWithPrimitive:primitiveValue]; }); 

块是要走的路。 你可以有复杂的参数,types安全,比这里的大多数旧的答案简单和安全。 例如,你可以写:

 [MONBlock performBlock:^{[obj setFrame:SOMETHING];} afterDelay:2]; 

块允许您捕获任意参数列表,引用对象和variables。

支持实施(基本):

 @interface MONBlock : NSObject + (void)performBlock:(void(^)())pBlock afterDelay:(NSTimeInterval)pDelay; @end @implementation MONBlock + (void)imp_performBlock:(void(^)())pBlock { pBlock(); } + (void)performBlock:(void(^)())pBlock afterDelay:(NSTimeInterval)pDelay { [self performSelector:@selector(imp_performBlock:) withObject:[pBlock copy] afterDelay:pDelay]; } @end 

例:

 int main(int argc, const char * argv[]) { @autoreleasepool { __block bool didPrint = false; int pi = 3; // close enough =p [MONBlock performBlock:^{NSLog(@"Hello, World! pi is %i", pi); didPrint = true;} afterDelay:2]; while (!didPrint) { [NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeInterval:0.1 sinceDate:NSDate.date]]; } NSLog(@"(Bye, World!)"); } return 0; } 

另请参阅Michael的回答(+1)。

PerformSelector:WithObject总是需要一个对象,所以为了传递像int / double / float等参数…..你可以使用这样的东西。

 //NSNumber is an object.. [self performSelector:@selector(setUserAlphaNumber:) withObject: [NSNumber numberWithFloat: 1.0f] afterDelay:1.5]; -(void) setUserAlphaNumber: (NSNumber*) number{ [txtUsername setAlpha: [number floatValue] ]; } 

同样的方式,你可以使用[NSNumber numberWithInt:]等….在接收方法,你可以转换为您的格式为[数字整数]或[数字双]的数字。

我会一直推荐你使用NSMutableArray作为传递的对象。 这是因为您可以传递几个对象,如按下button和其他值。 NSNumber,NSInteger和NSString只是一些有价值的容器。 确保从数组中引用正确的容器types的对象时。 你需要传递NS容器。 在那里你可以testing的价值。 请记住,比较值时容器使用isEqual。

 #define DELAY_TIME 5 -(void)changePlayerGameOnes:(UIButton*)sender{ NSNumber *nextPlayer = [NSNumber numberWithInt:[gdata.currentPlayer intValue]+1 ]; NSMutableArray *array = [[NSMutableArray alloc]initWithObjects:sender, nil]; [array addObject:nextPlayer]; [self performSelector:@selector(next:) withObject:array afterDelay:DELAY_TIME]; } -(void)next:(NSMutableArray*)nextPlayer{ if(gdata != nil){ //if game choose next player [self nextPlayer:[nextPlayer objectAtIndex:1] button:[nextPlayer objectAtIndex:0]]; } } 

我也想这样做,但是用一个接收BOOL参数的方法。 用NSNumber包装布尔值,失败通过价值。 我不知道为什么。

所以我最终做了一个简单的黑客。 我把所需的参数放在另一个虚拟函数中,并使用performSelector调用该函数,其中withObject = nil;

 [self performSelector:@selector(dummyCaller:) withObject:nil afterDelay:5.0]; -(void)dummyCaller { [self myFunction:YES]; } 

我发现最快(但有点脏)的方法是直接调用objc_msgSend。 然而,直接调用它是很危险的,因为你需要阅读文档,并确保你使用了正确的variablestypes的返回值,因为objc_msgSend被定义为vararg为编译器方便,但实际上被实现为快速assembly胶水。 下面是一些用于调用委托方法的代码 – [delegate integerDidChange:],它接受一个整数参数。

 #import <objc/message.h> SEL theSelector = @selector(integerDidChange:); if ([self.delegate respondsToSelector:theSelector]) { typedef void (*IntegerDidChangeFuncPtrType)(id, SEL, NSInteger); IntegerDidChangeFuncPtrType MyFunction = (IntegerDidChangeFuncPtrType)objc_msgSend; MyFunction(self.delegate, theSelector, theIntegerThatChanged); } 

这首先保存了select器,因为我们将多次引用它,并且很容易创build一个错字。 然后validation委托实际上是否响应select器 – 它可能是一个可选的协议。 然后创build一个函数指针types来指定select器的实际签名。 请记住,所有Objective-C消息都有两个隐藏的第一个参数,被消息对象和select器被发送。 然后我们创build一个适当types的函数指针,并将其设置为指向底层的objc_msgSend函数。 请记住,如果返回值是一个float或struct,则需要使用objc_msgSend的一个不同的变体。 最后,使用Objective-C在工作表下使用的相同机器发送消息。

你可以使用NSTimer来调用一个select器:

 [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(yourMethod:) userInfo:nil repeats:NO] 

使用NSNumber或其他NSValue调用performSelector将不起作用。 而不是使用NSValue / NSNumber的值,它将有效地将指针转换为int,float或其他任何并使用它。

但解决scheme简单明了。 创buildNSInvocation并调用

[invocation performSelector:@selector(invoke) withObject:nil afterDelay:delay]

Pehaps …好吧,很可能,我错过了一些东西,但是为什么不创build一个对象types,比如NSNumber,作为你的非对象typesvariables(比如CGFloat)的容器?

 CGFloat myFloat = 2.0; NSNumber *myNumber = [NSNumber numberWithFloat:myFloat]; [self performSelector:@selector(MyCalculatorMethod:) withObject:myNumber afterDelay:5.0];