Programing

Cocoa의 기본 유형과 함께 performSelector : withObject : afterDelay :를 사용하는 방법?

lottogame 2020. 8. 25. 19:17
반응형

Cocoa의 기본 유형과 함께 performSelector : withObject : afterDelay :를 사용하는 방법?


NSObject메서드를 performSelector:withObject:afterDelay:사용하면 일정 시간 후에 개체 인수를 사용하여 개체에 대한 메서드를 호출 할 수 있습니다. 객체가 아닌 인수 (예 : 정수, 부동 소수점, 구조체, 객체가 아닌 포인터 등)가있는 메서드에는 사용할 수 없습니다.

객체가 아닌 인수가있는 메서드로 동일한 작업을 수행 하는 가장 간단한 방법 은 무엇입니까 ? 나는 regular performSelector:withObject:의 경우 해결책이 사용하는 것임을 알고 있습니다 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];

float, boolean, int 또는 이와 유사한 것을 NSNumber로 감싸십시오.

구조체의 경우 편리한 솔루션을 모르지만 이러한 구조체를 소유하는 별도의 ObjC 클래스를 만들 수 있습니다.


이 답변을 사용하지 마십시오. 나는 역사적 목적으로 만 남겨 두었습니다. 아래 설명을 참조하십시오.

BOOL 매개 변수 인 경우 간단한 트릭이 있습니다.

아니오에 대해서는 nil을 전달하고 YES에 대해서는 self를 전달하십시오. nil은 BOOL 값 NO로 캐스트됩니다. 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 , 지연 후에도 포인터가 여전히 유효한지 확인하십시오 (즉, 스택에 할당 된 객체가 없음).


나는 이것이 오래된 질문이라는 것을 알고 있지만 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;
}

다른 예는 Michael의 대답 (+1)을 참조하십시오.


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.

#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]];
    }
}

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.

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;

[self performSelector:@selector(dummyCaller:) withObject:nil afterDelay:5.0];

-(void)dummyCaller {

[self myFunction:YES];

}

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.

#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.


You Could just use NSTimer to call a selector:

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

Calling performSelector with an NSNumber or other NSValue will not work. Instead of using the value of the NSValue/NSNumber, it will effectively cast the pointer to an int, float, or whatever and use that.

But the solution is simple and obvious. Create the NSInvocation and call

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


Pehaps...ok, very likely, I'm missing something, but why not just create an object type, say NSNumber, as a container to your non-object type variable, such as CGFloat?

CGFloat myFloat = 2.0; 
NSNumber *myNumber = [NSNumber numberWithFloat:myFloat];

[self performSelector:@selector(MyCalculatorMethod:) withObject:myNumber afterDelay:5.0];

참고URL : https://stackoverflow.com/questions/904515/how-to-use-performselectorwithobjectafterdelay-with-primitive-types-in-cocoa

반응형