Выполнение селекторов в главном потоке с NSInvocation - PullRequest
2 голосов
/ 03 июня 2010

Я хочу выполнить анимацию в основном потоке (потому что объекты UIKit не являются потокобезопасными), но подготовить его в отдельном потоке. У меня есть (baAnimation - CABasicAnimation выделен и инициирован раньше):

SEL animationSelector = @selector(addAnimation:forKey:);
NSString *keyString = @"someViewAnimation";

NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[workView.layer methodSignatureForSelector:animationSelector]];
[inv setTarget:workView.layer];
[inv setSelector:animationSelector];
[inv setArgument:baAnimation atIndex:2];
[inv setArgument:keyString atIndex:3];
[inv performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:NO];

Я получаю:

*** + [Длина NSCFString]: нераспознанный селектор отправлен в класс 0x1fb36a0

Звонки:

>     #0 0x020984e6 in objc_exception_throw
>     #1 0x01f7e8fb in +[NSObject doesNotRecognizeSelector:]
>     #2 0x01f15676 in ___forwarding___
>     #3 0x01ef16c2 in __forwarding_prep_0___
>     #4 0x01bb3c21 in -[CALayer addAnimation:forKey:]
>     #5 0x01ef172d in __invoking___
>     #6 0x01ef1618 in -[NSInvocation invoke]

Но [workView.layer addAnimation:baAnimation forKey:@"someViewAnimation"]; работает нормально. Что я делаю не так?

Ответы [ 3 ]

6 голосов
/ 16 сентября 2010

В дополнение к [inv retainArguments] (как упомянуто Крисом Сутером) вам также необходимо передать аргументы как указатели на базовую память. Ссылаясь на API:

"Если значение аргумента является объектом, передайте указатель на переменную (или память), из которой должен быть скопирован объект:

NSArray *anArray;  
[invocation setArgument:&anArray atIndex:3];  

"

4 голосов
/ 08 декабря 2011

Если у вас есть один или несколько аргументов в NSInvocation, я бы рекомендовал создать новую категорию, которая вызывает селектор в главном потоке. Вот как я решил это:

* ** 1003 тысяча два * Пример
NSInvocation + MainThread.h
#import <Foundation/Foundation.h>

@interface NSInvocation (MainThread)
- (void)invokeOnMainThreadWithTarget:(id)target;
@end

NSInvocation + MainThread.m

#import "NSInvocation+MainThread.h"

@implementation NSInvocation (MainThread)

- (void)invokeOnMainThreadWithTarget:(id)target {
    [self performSelectorOnMainThread:@selector(invokeWithTarget:) withObject:target waitUntilDone:YES];
}

@end
2 голосов
/ 03 июня 2010

Вам нужно либо добавить [inv retainArguments], либо изменить параметр waitUntilDone на YES, , но , прежде чем сделать это, позвольте мне сказать, что то, что вы сделали, довольно нечитабельно.

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

[self performSelectorOnMainThread:@selector (startAnimation) withObject:nil waitUntilDone:NO];

Кроме того, выделение и инициализация CABasicAnimation в потоке не нужны (это не займет заметного времени, чтобы сделать это в основном потоке), и все еще потенциально опасны. Сохраняйте интенсивную работу процессора в отдельном потоке, но не в другом.

...