objective-c - dispatch_time objective c




performSelector:withObject:afterDelay:코코아에서 기본 유형을 사용하는 방법? (9)

NSObject 메서드 performSelector:withObject:afterDelay: 특정 시간이 지나면 개체 인수로 개체의 메서드를 호출 할 수 있습니다. 객체가 아닌 인수 (예 : ints, float, structs, 비 객체 포인터 등)가있는 메소드에는 사용할 수 없습니다.

객체가 아닌 인수가있는 메소드를 사용하여 동일한 작업을 수행하는 가장 간단한 방법은 무엇입니까? 정규 performSelector:withObject: 에 대한 해결책은 NSInvocation 을 사용하는 것입니다 (이 과정은 실제로 복잡합니다). 그러나 나는 "지연"부분을 다루는 방법을 모른다.

감사,


NSNumber 또는 다른 NSValue를 사용하여 performSelector를 호출하면 작동하지 않습니다. NSValue / NSNumber 값을 사용하는 대신 효율적으로 포인터 를 int, float 등으로 캐스팅하고이를 사용합니다.

그러나 해결책은 간단하고 분명합니다. NSInvocation 만들기 및 호출

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


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 :] etc ...를 사용할 수 있습니다. 수신 방법에서는 숫자를 [number int] 또는 [number double] 형식으로 변환 할 수 있습니다.


나는 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가 시트 아래에서 사용하는 것과 동일한 기계를 사용하여 메시지를 보냅니다.


나는 항상 전달할 객체로 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로 bool 값을 감싸고, 값을 통과하지 못했습니다. 나는 이유를 모른다.

그래서 나는 간단한 해킹을 끝내었다. 필요한 매개 변수를 다른 더미 함수에 넣고 performSelector를 사용하여 해당 함수를 호출합니다 (where withObject = nil;

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

-(void)dummyCaller {

[self myFunction:YES];

}

블록은 갈 길이 멀다. 복잡한 매개 변수를 입력하고 안전성을 입력 할 수 있습니다. 대부분의 이전 답변보다 훨씬 간단하고 안전합니다. 예를 들어 다음과 같이 작성할 수 있습니다.

[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)을 참조하십시오.


아마도 ... 아마도, 뭔가 빠졌을 것입니다.하지만 NSNumber와 같은 객체 유형을 CGFloat와 같은 객체 유형이 아닌 변수의 컨테이너로 만들지 않는 이유는 무엇입니까?

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

[self performSelector:@selector(MyCalculatorMethod:) withObject:myNumber afterDelay:5.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];     
});




cocoa