Как использовать executeSelector: withObject: afterDelay: с примитивными типами в Какао?

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

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

Спасибо,

 Ramy Al Zuhouri19 июн. 2013 г., 18:43
Это немного глупо, но я считаю полезным писать быстрый код. Что-то вроде:id object = [array executeSelector: @selector (objectAtIndex :) withObject: (__bridge_transfer) (void *) 1]; , Если аргумент равен 0, вам даже не нужно использовать мост, потому что 0 «специальный» и id совместим с ним.

Ответы на вопрос(13)

логическое, int или подобное в NSNumber.

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

 Nicolas Miari26 июн. 2012 г., 19:31
Подтверждено:ДОЛЖЕН проходятnil дляBOOL параметр для полученияNO (FALSE)
 Peter Hosey24 мая 2009 г., 22:44
Вы можете использовать NSValue, чтобы обернуть структуру. В любом случае, NSNumber / NSValue является правильным решением.
 harms24 мая 2009 г., 22:01
Понял. Тогда я не уверен в элегантном решении, но вы могли бы добавить другой метод, который предназначен в качестве цели вашего executeSelector:, который распаковывает NSNumber и выполняет последний селектор для метода, который вы сейчас имеете в виду. Еще одна идея, которую вы могли бы изучить, - создать категорию для NSObject, в которую добавлено PerforSelector: withInt: ... (и тому подобное).
 James Williams24 мая 2009 г., 22:01
Создайте метод оболочки, -delayedMethodWithArgument: (NSNumber *) arg. Пусть он вызовет оригинальный метод после извлечения примитива из NSNumber.
 Jim Dovey24 мая 2009 г., 22:44
NSValue (суперкласс NSNumber) обернет все, что вы ему дадите. Если вы хотите обернуть экземпляр 'struct foo' с именем 'bar', вы должны сделать '[NSValue valueWithBytes: & bar objCType: @encode (struct foo)];'

Вместо использования значения NSValue / NSNumber он будет эффективно разыгрыватьуказатель к int, float или что-то еще и использовать это.

Но решение простое и очевидное. Создайте NSInvocation и вызовите

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

[NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(yourMethod:) userInfo:nil repeats:NO]
 Peter Hosey12 янв. 2010 г., 01:04
Вы упускаете аргумент. Аргумент кyourMethod: в вашем примере это сам таймер, и вы ничего не передали дляuserInfo, Даже если вы использовалиuserInfoВам все равно придется упаковать стоимость, как предположил Ремс и Ремус Русану.
 Applicasa iOS developer11 июн. 2012 г., 10:06
-1 для неиспользования executeSelector и создания ненужного экземпляра NSTimer

очень вероятно, я что-то упустил, но почему бы просто не создать тип объекта, скажем NSNumber, в качестве контейнера для вашей переменной не-объектного типа, такой как CGFloat?

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

[self performSelector:@selector(MyCalculatorMethod:) withObject:myNumber afterDelay:5.0];
 Alex Gray22 мар. 2013 г., 05:11
Вопрос предполагает, что OP имеет только внешний доступ к методу и не может изменить подпись.
 Rudolf Adamkovič30 сент. 2012 г., 17:39
Речь идет о передаче примитивных типов, а не объектов NSNumber.

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

 Alex Gray22 мар. 2013 г., 05:13
Будет ли любой старый метод "распаковать"NSValue (если возможно) к ожидаемомуtype?

что это старый вопрос, но если вы создаете 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];     
});
 Sebastien Martin12 февр. 2014 г., 19:44
Здесь вам не нужна слабая ссылка, потому что self не сохраняет блок (нет цикла сохранения). Вероятно, на самом деле предпочтительнее использовать сильную ссылку, поскольку в противном случае self может быть освобождено. Видеть:stackoverflow.com/a/19018633/251687
 Tim Wachter14 нояб. 2013 г., 11:03
Это создает цикл сохранения. Чтобы это работало правильно, вам нужно сделать слабую ссылку на себя и использовать эту ссылку для выполнения селектора. "__block typeof (self) weakSelf = self;"

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

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

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

-(void)dummyCaller {

[self myFunction:YES];

}
 Nicolas Miari26 июн. 2012 г., 19:36
Смотрите мой комментарий выше на ответ вреда. Похоже, так как-performSelector[...] метод ожидаетобъект (вместо примитивного типа данных), и он не знает, что вызываемый селектор принимает логическое значение, возможно, адрес (значение указателя)NSNumber экземпляр слепо «брошен» наBOOL (= не ноль, т.е.TRUE ). Возможно, время выполнения может быть умнее, чем это, и распознавать нулевой логический символ, завернутый вNSNumber!
 GeneCode27 июн. 2012 г., 13:16
Я согласен. Я думаю, что лучше всего избегать BOOL и использовать целое число 0 для NO и 1 для YES, и обернуть это с помощью NSNumber или NSValue.
 Nicolas Miari27 июн. 2012 г., 13:24
Это что-то меняет? Если это так, вдруг я действительно разочарован какао :)
Решение Вопроса

что я не мог изменить с помощью 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];
 Peter Hosey14 нояб. 2009 г., 19:07
Почему бы просто не сделать[theMovie setOrientation: UIInterfaceOrientationPortrait animated:NO]? Или вы имеете в виду, чтоinvoke сообщение в методе, который вы выполняете после задержки?
 Vishal Singh15 мая 2013 г., 08:20
просто примечание для тех, кто не знает, мы начинаем добавлять аргументы из индекса 2, потому что индексы 0 и 1 зарезервированы для скрытых аргументов "self" и "_cmd".
 Morty11 мая 2011 г., 18:11
Ах да, я забыл сказать .. `[anInvocation executeSelector: @selector (invoke) afterDelay: 0.5];

СМОТРИТЕ КОММЕНТАРИИ НИЖЕ.

Есть простой трюк, если это параметр BOOL.

Передайте ноль для НЕТ и себя для ДА. ноль приводится к значению BOOL NO. self приводится к значению BOOL YES.

Этот подход ломается, если это что-то отличное от параметра BOOL.

Предполагая, что я есть 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];
 Oleg Trakhman12 июл. 2012 г., 20:16
UPD:Никто не получит денег от ДА с вероятностью 6% из-за выравнивания памяти.
 user10200822 окт. 2011 г., 00:12
Я считаю, что значения BOOL всегда должны быть 0 или 1. Это правда, что большая часть кода не будет заботиться и просто сделаетif () протестируйте его, но возможно, что некоторый код будет зависеть от того, будет ли он равен 0 или 1, и, таким образом, не будет обрабатывать некоторое большое значение адреса как равное 1 (ДА).
 Farray19 июл. 2013 г., 05:16
@iVishal ПроверитьОстрые углы BOOL
 Oleg Trakhman12 июл. 2012 г., 19:32
НЕ ИСПОЛЬЗУЙТЕ ЭТОГО, Этот подход является источником серьезных ошибок, которые трудно найти. Представитьself с адресом как0x0123400, С этим приближением, вы получитеНЕТ от ДА в 0,4% случаев. Хуже того, с такой вероятностью это решение может пройти все испытания и раскрыться позже.
 0xSina21 апр. 2012 г., 05:30
Спасибо за это, я не хотел создавать обертку или писать уродливый синтаксис GCD для этого.

что самый быстрый (но несколько грязный) способ сделать это - вызвать 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 использует под листами.

 user10200824 апр. 2012 г., 01:31
«Имейте в виду, что если вы отправляете поплавки или структуры ...», вы имеете в видувозврате плавает или строит
 Jason Harris10 мая 2012 г., 01:48
Эти три строки обеспечивают безопасность типов и гарантируют, что количество аргументов - это то, что ожидает ваш селектор. objc_msgSend - это функция vararg, которая на самом деле реализована в виде клея для сборки, поэтому, если вы не используете typecast, вы можете дать ей что угодно.

а для передачи. Это потому, что вы можете затем передать несколько объектов, таких как нажатая кнопка и другие значения. 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]];
    }
}

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

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

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] или [число двойное].

 big_m27 окт. 2013 г., 19:57
Если кто-то ограничен до + executeSelector: withObject: +, то это единственный правильный ответ опубликован, IMO. Но ответ @ MichaelGaylord с использованием блоков будет чище, если это вариант для вас.

Ваш ответ на вопрос