代码之家  ›  专栏  ›  技术社区  ›  kpower

在带有nsinvocation的主线程上执行选择器

  •  2
  • kpower  · 技术社区  · 15 年前

    我想在主线程上执行动画(因为uikit对象不是线程安全的),但要在一些单独的线程中进行准备。我有(Baanimation-以前分配过和初始化过Cabasicanization):

    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"]; 工作良好。我做错什么了?

    3 回复  |  直到 13 年前
        1
  •  6
  •   hennes    14 年前

    除了[inv retainarguments]之外(如ChrisSuter所述),您还需要将参数作为指向底层内存的指针传递。引用API:

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

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

        2
  •  4
  •   iTuna    13 年前

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

    例子
    nsinvocation+mainthread.h(位置+mainthread.h)

    #import <Foundation/Foundation.h>
    
    @interface NSInvocation (MainThread)
    - (void)invokeOnMainThreadWithTarget:(id)target;
    @end
    

    nsinvocation+mainthread.m(位置+mainthread.m)

    #import "NSInvocation+MainThread.h"
    
    @implementation NSInvocation (MainThread)
    
    - (void)invokeOnMainThreadWithTarget:(id)target {
        [self performSelectorOnMainThread:@selector(invokeWithTarget:) withObject:target waitUntilDone:YES];
    }
    
    @end
    
        3
  •  2
  •   Chris Suter    15 年前

    您要么需要添加 [inv retainArguments] 或者将waituntildone参数更改为yes, 但是 在你这么做之前,让我先说一下,你所做的是相当不可读的。

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

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

    另外,在线程上分配和初始化一个cabasicanimation是不必要的(在主线程上这样做不会花费任何明显的时间),而且仍然是潜在的危险。将处理器密集型工作保留在单独的线程上,而不是其他任何线程上。