Как использовать executeSelector: withObject: afterDelay: с примитивными типами в Какао? - PullRequest
96 голосов
/ 24 мая 2009

Метод NSObject performSelector:withObject:afterDelay: позволяет мне вызывать метод объекта с аргументом объекта через определенное время. Его нельзя использовать для методов с необъектным аргументом (например, целыми числами, числами с плавающей точкой, структурами, необъектными указателями и т. Д.).

Каков простой способ добиться того же с помощью метода с необъектным аргументом? Я знаю, что для обычного performSelector:withObject: решение состоит в том, чтобы использовать NSInvocation (что, кстати, действительно сложно). Но я не знаю, как справиться с «задержкой».

Спасибо

Ответы [ 13 ]

69 голосов
/ 14 ноября 2009

Вот что я использовал для вызова чего-то, что я не мог изменить с помощью 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];
35 голосов
/ 24 мая 2009

Просто оберните число с плавающей точкой, логическое, int или подобное в NSNumber.

Для структур, я не знаю удобного решения, но вы могли бы создать отдельный класс ObjC, которому принадлежит такая структура.

13 голосов
/ 21 октября 2011

НЕ ИСПОЛЬЗУЙТЕ ЭТО ОТВЕТ. Я оставил его только для исторических целей. СМОТРИТЕ КОММЕНТАРИИ НИЖЕ.

Существует простой трюк, если это параметр BOOL.

Передайте ноль для НЕТ, а себя для ДА. ноль приводится к значению 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];
8 голосов
/ 19 марта 2012

Я знаю, что это старый вопрос, но если вы создаете 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];     
});
8 голосов
/ 25 мая 2009

Возможно NSValue , просто убедитесь, что ваши указатели остаются действительными после задержки (т. Е. В стеке нет объектов, выделенных).

6 голосов
/ 21 августа 2013

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:] и т. Д .... и в методе получения вы можете преобразовать число в ваш формат как [число int] или [число двойное].

5 голосов
/ 12 июля 2013

Блоки - это путь. У вас могут быть сложные параметры, безопасность типов, и это намного проще и безопаснее, чем большинство старых ответов здесь. Например, вы можете просто написать:

[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) для другого примера.

3 голосов
/ 17 марта 2014

Я бы всегда рекомендовал использовать 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]];
    }
}
2 голосов
/ 24 апреля 2012

Я считаю, что самый быстрый (но несколько грязный) способ сделать это - вызвать objc_msgSend напрямую. Однако вызывать его напрямую опасно, потому что вам нужно прочитать документацию и убедиться, что вы используете правильный вариант для типа возвращаемого значения, а также потому, что objc_msgSend определен как vararg для удобства компилятора, но фактически реализован как клей для быстрой сборки , Вот некоторый код, используемый для вызова метода делегата - [делегат 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 или структурой, вам нужно использовать другой вариант objc_msgSend. Наконец, отправьте сообщение, используя тот же механизм, который Objective-C использует под листами.

2 голосов
/ 22 февраля 2011

Я тоже хотел сделать это, но с методом, который получает параметр BOOL. Оборачивая значение bool с помощью NSNumber, FAILED TO PASS VALUE. Понятия не имею почему.

Так что я закончил простым хаком. Я помещаю обязательный параметр в другую фиктивную функцию и вызываю эту функцию с помощью executeSelector, где withObject = nil;

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

-(void)dummyCaller {

[self myFunction:YES];

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...