Правильный способ передачи примитивного аргумента с помощью NSTimer - PullRequest
2 голосов
/ 16 июля 2010

Я использую базовый таймер, который вызывает этот метод:

- (void) refresh:(id)obj
{
    if (obj == YES) doSomething;
}

Я хочу вызвать этот метод из определенных областей моего кода, а также из таймера

[NSTimer scheduledTimerWithTimeInterval:refreshInterval
                            target:self
                            selector:@selector(refresh:) 
                            userInfo:nil 
                            repeats:YES];

Когда я ставлю YES в качестве аргумента для параметра userInfo, я получаю ошибку EXC_BAD_ACCESS; почему это?

Может ли кто-нибудь помочь мне сделать это правильно, чтобы не было уродливого кастинга и тому подобное?

Ответы [ 3 ]

10 голосов
/ 16 июля 2010

Параметр userInfo должен быть объектом; набрано id. YES является примитивом, а именно значением 1. Чтобы убедиться, что объект userInfo не освобожден, таймер сохраняет его. Итак, когда вы прошли YES, NSTimer делал [(id)YES retain]. Попробуйте это в своем собственном коде и посмотрите, что произойдет. : -Р

Как указано в Документации , селектор, который вы передаете методу, должен иметь подпись

- (void)timerFireMethod:(NSTimer*)theTimer

Это означает, что вы не можете заставить NSTimer вызывать какой-либо метод - по крайней мере, напрямую. Вы можете создать специальный метод с указанной выше сигнатурой, который, в свою очередь, вызывает любой метод, который вы хотите.

Итак, скажем, у вас есть метод с именем refresh:, и вы хотите вызывать его время от времени, передавая YES. Вы можете сделать это так:

// somewhere
{
    [NSTimer scheduledTimerWithTimeInterval:refreshInterval
                                     target:self
                                   selector:@selector(invokeRefresh:)
                                   userInfo:nil
                                    repeats:YES];
}

- (void)invokeRefresh:(NSTimer *)timer {
    [self refresh:YES];
}

- (void)refresh:(BOOL)flag {
    if (flag) {
        // do something
    }
}
4 голосов
/ 16 июля 2010

В Objective-C примитивный тип не является объектом. Таким образом, вы не можете напрямую передать его аргументу, который ожидает id, что означает универсальный объект. Вам нужно обернуть его в NSNumber объект.

Используйте

NSTimer*timer=[NSTimer scheduledTimerWithTimeInterval:refreshInterval
                                 target:self
                               selector:@selector(refresh:) 
                               userInfo:[NSNumber numberWithBool:YES]
                                repeats:YES];

и

- (void) refresh:(NSTimer*)timer
{
    NSNumber* shouldDoSomething=[timer userInfo];
    if ([shouldDoSomething boolValue]) doSomething;
}

Не забудьте аннулировать и отпустить таймер, как только это будет сделано.

Кстати, вам не нужно сравнивать BOOL (или C ++ bool) с YES или true или чем-то еще. Когда вы пишете

if(a>b) { ... }

a>b оценивает как bool, а if использует результат. То, что вы там делаете, похоже на

if((a>b)==YES) { ... }

, что довольно странно для меня. Нельзя сказать, что (..) после if должно содержать сравнение; он должен содержать bool.

0 голосов
/ 16 июля 2010

В качестве продолжения ответа kperryua, если вы хотите передать примитив через userInfo, вы можете пометить его как NSNumber или NSValue;в случае логического значения вы хотите использовать [NSNumber numberWithBool:YES], а затем вызвать boolValue в обратном вызове таймера, чтобы вернуть примитив.

...