objective-c 如何在 Cocoa 中使用 performSelector:withObject:afterDelay: 原始类型?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/904515/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to use performSelector:withObject:afterDelay: with primitive types in Cocoa?
提问by user102008
The NSObjectmethod performSelector:withObject:afterDelay:allows me to invoke a method on the object with an object argument after a certain time. It cannot be used for methods with a non-object argument (e.g. ints, floats, structs, non-object pointers, etc.).
该NSObject方法performSelector:withObject:afterDelay:允许我在一段时间后使用对象参数调用对象上的方法。它不能用于带有非对象参数的方法(例如整数、浮点数、结构体、非对象指针等)。
What is the simplestway to achieve the same thing with a method with a non-object argument? I know that for regular performSelector:withObject:, the solution is to use NSInvocation(which by the way is really complicated). But I don't know how to handle the "delay" part.
使用具有非对象参数的方法实现相同目的的最简单方法是什么?我知道对于常规performSelector:withObject:,解决方案是使用NSInvocation(顺便说一句,这真的很复杂)。但我不知道如何处理“延迟”部分。
Thanks,
谢谢,
采纳答案by Morty
Here is what I used to call something I couldn't change using 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];
回答by harms
Just wrap the float, boolean, int or similar in an NSNumber.
只需将 float、boolean、int 或类似的内容包装在 NSNumber 中。
For structs, I don't know of a handy solution, but you could make a separate ObjC class that owns such a struct.
对于结构,我不知道有什么方便的解决方案,但是您可以创建一个单独的 ObjC 类来拥有这样的结构。
回答by harms
DO NOT USE THIS ANSWER. I HAVE ONLY LEFT IT FOR HISTORICAL PURPOSES. SEE THE COMMENTS BELOW.
不要使用这个答案。我只是把它留作历史用途。请参阅下面的评论。
There is a simple trick if it is a BOOL parameter.
如果它是 BOOL 参数,则有一个简单的技巧。
Pass nil for NO and self for YES. nil is cast to the BOOL value of NO. self is cast to the BOOL value of YES.
为 NO 传递 nil,为 YES 传递 self。nil 被转换为 NO 的 BOOL 值。self 被转换为 YES 的 BOOL 值。
This approach breaks down if it is anything other than a BOOL parameter.
如果它不是 BOOL 参数,则此方法会失效。
Assuming self is a UIView.
假设 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];
回答by Michael Gaylord
I know this is an old question but if you are building iOS SDK 4+ then you can use blocks to do this with very little effort and make it more readable:
我知道这是一个老问题,但是如果您正在构建 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];
});
回答by Remus Rusanu
回答by Augustine
PerformSelector:WithObject always takes an object, so in order to pass arguments like int/double/float etc..... You can use something like this.
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] ];
}
Same way you can use [NSNumber numberWithInt:] etc.... and in the receiving method you can convert the number into your format as [number int] or [number double].
同样的方式你可以使用 [NSNumber numberWithInt:] 等......并且在接收方法中你可以将数字转换为 [number int] 或 [number double] 的格式。
回答by justin
Blocks are the way to go. You can have complex parameters, type safety, and it's a lot simpler and safer than most of the old answers here. For example, you could just write:
块是要走的路。您可以拥有复杂的参数,类型安全,并且比这里的大多数旧答案更简单和安全。例如,你可以只写:
[MONBlock performBlock:^{[obj setFrame:SOMETHING];} afterDelay:2];
Blocks allow you to capture arbitrary parameter lists, reference objects and variables.
块允许您捕获任意参数列表、引用对象和变量。
Backing Implementation (basic):
支持实现(基本):
@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
Example:
例子:
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;
}
Also see Michael's answer (+1) for another example.
另请参阅迈克尔的回答 (+1) 以获取另一个示例。
回答by Odd-Arne
I would always recomend that you use NSMutableArray as the object to pass on. This is because you can then pass several objects, like the button pressed and other values. NSNumber, NSInteger and NSString are just containers of some value. Make sure that when you get the object from the array that you refer to to a correct container type. You need to pass on NS containers. There you may test the value. Remember that containers use isEqual when values are compared.
我总是建议您使用 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]];
}
}
回答by Jason Harris
I find that the quickest (but somewhat dirty) way to do this is by invoking objc_msgSend directly. However, it's dangerous to invoke it directly because you need to read the documentation and make sure that you're using the correct variant for the type of return value and because objc_msgSend is defined as vararg for compiler convenience but is actually implemented as fast assembly glue. Here's some code used to call a delegate method -[delegate integerDidChange:] that takes a single integer argument.
我发现最快(但有点脏)的方法是直接调用 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);
}
This first saves the selector since we're going to refer to it multiple times and it would be easy to create a typo. It then verifies that the delegate actually responds to the selector - it might be an optional protocol. It then creates a function pointer type that specifies the actual signature of the selector. Keep in mind that all Objective-C messages have two hidden first arguments, the object being messaged and the selector being sent. Then we create a function pointer of the appropriate type and set it to point to the underlying objc_msgSend function. Keep in mind that if the return value is a float or struct, you need to use a different variant of objc_msgSend. Finally, send the message using the same machinery that Objective-C uses under the sheets.
这首先保存了选择器,因为我们将多次引用它,并且很容易创建拼写错误。然后验证委托实际上响应了选择器——它可能是一个可选协议。然后它创建一个函数指针类型,指定选择器的实际签名。请记住,所有的 Objective-C 消息都有两个隐藏的第一个参数,被发送消息的对象和被发送的选择器。然后我们创建一个适当类型的函数指针,并将其设置为指向底层 objc_msgSend 函数。请记住,如果返回值是浮点数或结构体,则需要使用 objc_msgSend 的不同变体。最后,使用与 Objective-C 在工作表下使用的相同机制发送消息。
回答by GeneCode
I also wanted to do this, but with a method that receives a BOOL parameter. Wrapping the bool value with NSNumber, FAILED TO PASS THE VALUE. I have no idea why.
我也想这样做,但是使用一个接收 BOOL 参数的方法。用 NSNumber 包装 bool 值,无法传递值。我不知道为什么。
So I ended up doing a simple hack. I put the required parameter in another dummy function and call that function using the performSelector, where withObject = nil;
所以我最终做了一个简单的黑客。我将所需的参数放在另一个虚拟函数中,并使用 performSelector 调用该函数,其中 withObject = nil;
[self performSelector:@selector(dummyCaller:) withObject:nil afterDelay:5.0];
-(void)dummyCaller {
[self myFunction:YES];
}

