我得到ARC编译器的以下警告:

"performSelector may cause a leak because its selector is unknown".

这是我正在做的:

[_controller performSelector:NSSelectorFromString(@"someMethod")];

为什么我得到这个警告?我理解编译器不能检查选择器是否存在,但为什么会导致泄漏?我怎样才能改变我的代码,让我不再得到这个警告呢?


当前回答

您还可以在这里使用协议。因此,创建一个这样的协议:

@protocol MyProtocol
-(void)doSomethingWithObject:(id)object;
@end

在需要调用选择器的类中,有一个@属性。

@interface MyObject
    @property (strong) id<MyProtocol> source;
@end

当你需要在MyObject的实例中调用@selector(doSomethingWithObject:)时,这样做:

[self.source doSomethingWithObject:object];

其他回答

在Xcode 4.2的LLVM 3.0编译器中,你可以像下面这样抑制警告:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    [self.ticketTarget performSelector: self.ticketAction withObject: self];
#pragma clang diagnostic pop

如果你在几个地方得到错误,并且想要使用C宏系统来隐藏指令,你可以定义一个宏来更容易地抑制警告:

#define SuppressPerformSelectorLeakWarning(Stuff) \
    do { \
        _Pragma("clang diagnostic push") \
        _Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \
        Stuff; \
        _Pragma("clang diagnostic pop") \
    } while (0)

你可以像这样使用宏:

SuppressPerformSelectorLeakWarning(
    [_target performSelector:_action withObject:self]
);

如果你需要执行消息的结果,你可以这样做:

id result;
SuppressPerformSelectorLeakWarning(
    result = [_target performSelector:_action withObject:self]
);

如果你不需要传递任何参数,一个简单的解决方法是使用valueForKeyPath。这甚至可以在Class对象上实现。

NSString *colorName = @"brightPinkColor";
id uicolor = [UIColor class];
if ([uicolor respondsToSelector:NSSelectorFromString(colorName)]){
    UIColor *brightPink = [uicolor valueForKeyPath:colorName];
    ...
}

这段代码不涉及编译器标志或直接运行时调用:

SEL selector = @selector(zeroArgumentMethod);
NSMethodSignature *methodSig = [[self class] instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setSelector:selector];
[invocation setTarget:self];
[invocation invoke];

NSInvocation允许设置多个参数,因此与performSelector不同,这将适用于任何方法。

我没有使用块方法,这给我带来了一些问题:

    IMP imp = [_controller methodForSelector:selector];
    void (*func)(id, SEL) = (void *)imp;

我将使用NSInvocation,像这样:

    -(void) sendSelectorToDelegate:(SEL) selector withSender:(UIButton *)button 

    if ([delegate respondsToSelector:selector])
    {
    NSMethodSignature * methodSignature = [[delegate class]
                                    instanceMethodSignatureForSelector:selector];
    NSInvocation * delegateInvocation = [NSInvocation
                                   invocationWithMethodSignature:methodSignature];


    [delegateInvocation setSelector:selector];
    [delegateInvocation setTarget:delegate];

    // remember the first two parameter are cmd and self
    [delegateInvocation setArgument:&button atIndex:2];
    [delegateInvocation invoke];
    }

您还可以在这里使用协议。因此,创建一个这样的协议:

@protocol MyProtocol
-(void)doSomethingWithObject:(id)object;
@end

在需要调用选择器的类中,有一个@属性。

@interface MyObject
    @property (strong) id<MyProtocol> source;
@end

当你需要在MyObject的实例中调用@selector(doSomethingWithObject:)时,这样做:

[self.source doSomethingWithObject:object];