NSObject
方法performSelector:withObject:afterDelay:
允许我在一定时间后用对象参数调用对象上的方法。它不能用于具有非对象参数的方法(例如,整数,浮点数,结构,非对象指针等)。
使用非对象参数的方法实现同样的事情的最简单方法是什么?我知道对于常规performSelector:withObject:
,解决方案是使用NSInvocation
(顺便说一句,这非常复杂)。但我不知道如何处理“延迟”部分。
谢谢,
答案 0 :(得分:69)
以下是我过去常常使用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];
答案 1 :(得分:35)
只需在NSNumber中包装float,boolean,int或类似内容。
对于结构体,我不知道一个方便的解决方案,但你可以创建一个拥有这样一个结构的单独的ObjC类。
答案 2 :(得分:13)
请勿使用此答案。我只是为了历史目的而离开了它。请参阅下面的评论。
如果它是BOOL参数,有一个简单的技巧。
通过n为NO而self为YES。 nil被转换为NO的BOOL值。 self被转换为BOOL值为YES。
如果它不是BOOL参数,则该方法会失效。
假设自己是一个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];
答案 3 :(得分:8)
也许NSValue,只是确保你的指针在延迟后仍然有效(即没有在堆栈上分配对象)。
答案 4 :(得分:8)
我知道这是一个老问题,但如果您正在构建iOS SDK 4+,那么您可以使用块来轻松完成这项工作并使其更具可读性:
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];
});
答案 5 :(得分:6)
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:]等....在接收方法中你可以将数字转换成格式为[number int]或[number double]。
答案 6 :(得分:5)
块是可行的方法。您可以拥有复杂的参数,类型安全,并且它比这里的大多数旧答案更简单,更安全。例如,你可以写:
[MONBlock performBlock:^{[obj setFrame:SOMETHING];} afterDelay:2];
Blocks允许您捕获任意参数列表,引用对象和变量。
支持实施(基本):
@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)。
答案 7 :(得分:3)
我总是建议您使用NSMutableArray作为要传递的对象。这是因为您可以传递多个对象,例如按下的按钮和其他值。 NSNumber,NSInteger和NSString只是一些有价值的容器。从阵列中获取对象时确保 您引用正确的容器类型。你需要传递NS容器。你可以测试一下这个值。请记住,在比较值时,容器使用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]];
}
}
答案 8 :(得分:2)
我也想这样做,但是使用一个接收BOOL参数的方法。用NSNumber包装bool值,未通过该值。我不明白为什么。
所以我最后做了一个简单的黑客攻击。我将所需的参数放在另一个虚函数中并使用performSelector调用该函数,其中withObject = nil;
[self performSelector:@selector(dummyCaller:) withObject:nil afterDelay:5.0];
-(void)dummyCaller {
[self myFunction:YES];
}
答案 9 :(得分:2)
我发现最快(但有点脏)的方法是直接调用objc_msgSend。但是,直接调用它是危险的,因为您需要阅读文档并确保使用正确的变量来表示返回值的类型,并且因为objc_msgSend被定义为vararg以便于编译,但实际上是作为快速汇编胶实现的。下面是一些用于调用委托方法的代码 - [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);
}
这首先保存选择器,因为我们将多次引用它,并且很容易创建拼写错误。然后它验证委托实际响应选择器 - 它可能是一个可选协议。然后它创建一个函数指针类型,指定选择器的实际签名。请记住,所有Objective-C消息都有两个隐藏的第一个参数,正在发送消息的对象和正在发送的选择器。然后我们创建一个适当类型的函数指针,并将其设置为指向底层的objc_msgSend函数。请记住,如果返回值是float或struct,则需要使用objc_msgSend的不同变体。最后,使用Objective-C在工作表下使用的相同机制发送消息。
答案 10 :(得分:1)
您可以使用NSTimer来调用选择器:
[NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(yourMethod:) userInfo:nil repeats:NO]
答案 11 :(得分:1)
使用NSNumber或其他NSValue调用performSelector将不起作用。它不会使用NSValue / NSNumber的值,而是将指针有效地转换为int,float或其他任何内容并使用它。
但解决方案很简单明了。创建NSInvocation并调用
[invocation performSelector:@selector(invoke) withObject:nil afterDelay:delay]
答案 12 :(得分:0)
Pehaps ...好吧,很可能,我错过了什么,但为什么不创建一个对象类型,比如说NSNumber,作为非对象类型变量的容器,比如CGFloat?
CGFloat myFloat = 2.0;
NSNumber *myNumber = [NSNumber numberWithFloat:myFloat];
[self performSelector:@selector(MyCalculatorMethod:) withObject:myNumber afterDelay:5.0];