使用 NSIncation 在主线程上执行选择器

发布于 2024-09-03 14:57:43 字数 1198 浏览 3 评论 0原文

我想在主线程上执行动画(因为 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 length]:无法识别的选择器发送到类 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"]; 工作正常。我做错了什么?

I want to perform animation on main thread (cause UIKit objects are not thread-safe), but prepare it in some separate thread. I have (baAnimation - is CABasicAnimation allocated & inited before):

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];

I get:

*** +[NSCFString length]: unrecognized selector sent to class 0x1fb36a0

Calls:

>     #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]

But [workView.layer addAnimation:baAnimation forKey:@"someViewAnimation"]; works fine. What am I doing wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

情话墙 2024-09-10 14:57:43

除了 [inv keepArguments](如 Chris Suter 提到的)之外,您还需要将参数作为指向底层内存的指针传递。引用 API:

“当参数值是对象时,将指针传递给应从中复制对象的变量(或内存):

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

In addition to [inv retainArguments] (as mentioned by Chris Suter) you also need to pass the arguments as pointers to the underlying memory. Citing the API:

"When the argument value is an object, pass a pointer to the variable (or memory) from which the object should be copied:

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

"

方觉久 2024-09-10 14:57:43

如果您的 NSInitation 中有一个或多个参数,那么我建议创建一个新类别来调用主线程上的选择器。这就是我解决这个问题的方法:

示例
NSInitation+MainThread.h

#import <Foundation/Foundation.h>

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

NSInitation+MainThread.m

#import "NSInvocation+MainThread.h"

@implementation NSInvocation (MainThread)

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

@end

If you have one or more arguments in your NSInvocation then I would recommend creating a new category that invokes the selector on main thread. This is how I solved this:

Example
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
泅渡 2024-09-10 14:57:43

您需要添加 [inv keepArguments] 或将 waitUntilDone 参数更改为 YES,但是在执行此操作之前,我只想说您所做的事情非常难以理解。

我要做的就是将您需要的任何状态存储在实例变量中,然后当您准备好时,只需执行以下操作:

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

同时分配并且在线程上初始化 CABasicAnimation 是不必要的(在主线程上执行此操作不会花费任何明显的时间),并且仍然存在潜在危险。将处理器密集型工作保留在单独的线程上,而不是其他任何事情。

You either need to add [inv retainArguments] or change the waitUntilDone parameter to YES, but before you do that, let me just say that what you’ve done is pretty unreadable.

What I would do is store whatever state you need in instance variables and then when you're ready, just do:

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

Also allocating and initialising a CABasicAnimation on a thread is unnecessary (it won't take any noticeable time to do it on the main thread), and is still potentially dangerous. Keep processor intensive work on a separate thread, but not anything else.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文