该NSObject
方法performSelector:withObject:afterDelay:
允许我在一定时间后使用对象参数调用对象上的方法。它不能用于带有非对象参数的方法(例如,整数,浮点数,结构,非对象指针等)。
使用带有非对象参数的方法来实现相同目的的最简单方法是什么?我知道对于regular performSelector:withObject:
,解决方案是使用NSInvocation
(顺便说一句,这确实很复杂)。但是我不知道如何处理“延迟”部分。
谢谢,
该NSObject
方法performSelector:withObject:afterDelay:
允许我在一定时间后使用对象参数调用对象上的方法。它不能用于带有非对象参数的方法(例如,整数,浮点数,结构,非对象指针等)。
使用带有非对象参数的方法来实现相同目的的最简单方法是什么?我知道对于regular performSelector:withObject:
,解决方案是使用NSInvocation
(顺便说一句,这确实很复杂)。但是我不知道如何处理“延迟”部分。
谢谢,
Answers:
这是我过去使用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];
[theMovie setOrientation: UIInterfaceOrientationPortrait animated:NO]
呢?还是说该invoke
消息处于延迟执行方法中?
只需将float,boolean,int或类似内容包装在NSNumber中即可。
对于结构体,我不知道有什么方便的解决方案,但是您可以创建一个拥有这样结构体的独立ObjC类。
nil
一个BOOL
参数来接收NO
(FALSE
)
请勿使用此答案。我仅出于历史目的保留它。请参阅下面的评论。
如果它是BOOL参数,则有一个简单的技巧。
传递nil表示否,传递self表示是。nil强制转换为BOOL值NO。将self强制转换为BOOL值YES。
如果不是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];
if ()
测试,但是可以想象,某些代码将依赖于它为0或1,因此可以获胜。不要将某个较大的地址值视为与1相同(是)。
self
地址为0x0123400
。有了这个方法,您将在0.4%的情况下获得YES。更糟糕的是,这种解决方案可能会通过所有测试,并在以后揭晓。
我知道这是一个老问题,但是如果您要构建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];
});
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]格式。
障碍是要走的路。您可以使用复杂的参数,输入安全性,并且比这里的大多数旧答案更简单,更安全。例如,您可以编写:
[MONBlock performBlock:^{[obj setFrame:SOMETHING];} afterDelay:2];
块允许您捕获任意参数列表,参考对象和变量。
支持实施(基本):
@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;
}
另请参见迈克尔的答案(+1)。
我总是建议您使用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]];
}
}
我也想这样做,但是要使用一种接收BOOL参数的方法。用NSNumber包装布尔值,则无法通过该值。我不知道为什么。
所以我最终做了一个简单的修改。我将所需的参数放在另一个虚拟函数中,并使用performSelector调用该函数,其中withObject = nil;
[self performSelector:@selector(dummyCaller:) withObject:nil afterDelay:5.0];
-(void)dummyCaller {
[self myFunction:YES];
}
-performSelector[...]
方法需要一个对象(而不是原始数据类型),并且它不知道被调用的选择器接受布尔值,所以NSNumber
实例的地址(指针值)可能被盲目地“投射”到BOOL
(=非零,即TRUE
)。也许运行时可能比这更聪明,并且可以识别包装在NSNumber
!中的零布尔值!
我发现最快的方法(但有些脏)是直接调用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函数。请记住,如果返回值是浮点型或结构型,则需要使用objc_msgSend的其他变体。最后,使用工作表下的Objective-C使用的相同机器发送消息。
您可以只使用NSTimer来调用选择器:
[NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(yourMethod:) userInfo:nil repeats:NO]
yourMethod:
在您的示例中,to的参数是计时器本身,并且您没有为传递任何东西userInfo
。即使您确实使用过userInfo
,也仍然必须按照危害和Remus Rusanu的建议包装价值。
可能...好吧,很可能我遗漏了一些东西,但是为什么不创建一个对象类型(例如NSNumber)作为非对象类型变量(例如CGFloat)的容器呢?
CGFloat myFloat = 2.0;
NSNumber *myNumber = [NSNumber numberWithFloat:myFloat];
[self performSelector:@selector(MyCalculatorMethod:) withObject:myNumber afterDelay:5.0];