I\'m getting the following warning by the ARC compiler:
\"performSelector may cause a leak because its selector is unknown\".
Here\'s what
Instead of using the block approach, which gave me some problems:
IMP imp = [_controller methodForSelector:selector];
void (*func)(id, SEL) = (void *)imp;
I will use NSInvocation, like this:
-(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];
}
This code doesn't involve compiler flags or direct runtime calls:
SEL selector = @selector(zeroArgumentMethod);
NSMethodSignature *methodSig = [[self class] instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setSelector:selector];
[invocation setTarget:self];
[invocation invoke];
NSInvocation
allows multiple arguments to be set so unlike performSelector
this will work on any method.
Matt Galloway's answer on this thread explains the why:
Consider the following:
id anotherObject1 = [someObject performSelector:@selector(copy)]; id anotherObject2 = [someObject performSelector:@selector(giveMeAnotherNonRetainedObject)];
Now, how can ARC know that the first returns an object with a retain count of 1 but the second returns an object which is autoreleased?
It seems that it is generally safe to suppress the warning if you are ignoring the return value. I'm not sure what the best practice is if you really need to get a retained object from performSelector -- other than "don't do that".
In your project Build Settings, under Other Warning Flags (WARNING_CFLAGS
), add
-Wno-arc-performSelector-leaks
Now just make sure that the selector you are calling does not cause your object to be retained or copied.
Well, lots of answers here, but since this is a little different, combining a few answers I thought I'd put it in. I'm using an NSObject category which checks to make sure the selector returns void, and also suppresses the compiler warning.
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "Debug.h" // not given; just an assert
@interface NSObject (Extras)
// Enforce the rule that the selector used must return void.
- (void) performVoidReturnSelector:(SEL)aSelector withObject:(id)object;
- (void) performVoidReturnSelector:(SEL)aSelector;
@end
@implementation NSObject (Extras)
// Apparently the reason the regular performSelect gives a compile time warning is that the system doesn't know the return type. I'm going to (a) make sure that the return type is void, and (b) disable this warning
// See http://stackoverflow.com/questions/7017281/performselector-may-cause-a-leak-because-its-selector-is-unknown
- (void) checkSelector:(SEL)aSelector {
// See http://stackoverflow.com/questions/14602854/objective-c-is-there-a-way-to-check-a-selector-return-value
Method m = class_getInstanceMethod([self class], aSelector);
char type[128];
method_getReturnType(m, type, sizeof(type));
NSString *message = [[NSString alloc] initWithFormat:@"NSObject+Extras.performVoidReturnSelector: %@.%@ selector (type: %s)", [self class], NSStringFromSelector(aSelector), type];
NSLog(@"%@", message);
if (type[0] != 'v') {
message = [[NSString alloc] initWithFormat:@"%@ was not void", message];
[Debug assertTrue:FALSE withMessage:message];
}
}
- (void) performVoidReturnSelector:(SEL)aSelector withObject:(id)object {
[self checkSelector:aSelector];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
// Since the selector (aSelector) is returning void, it doesn't make sense to try to obtain the return result of performSelector. In fact, if we do, it crashes the app.
[self performSelector: aSelector withObject: object];
#pragma clang diagnostic pop
}
- (void) performVoidReturnSelector:(SEL)aSelector {
[self checkSelector:aSelector];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self performSelector: aSelector];
#pragma clang diagnostic pop
}
@end
For posterity's sake, I've decided to throw my hat into the ring :)
Recently I've been seeing more and more restructuring away from the target
/selector
paradigm, in favor of things such as protocols, blocks, etc. However, there is one drop-in replacement for performSelector
that I've used a few times now:
[NSApp sendAction: NSSelectorFromString(@"someMethod") to: _controller from: nil];
These seem to be a clean, ARC-safe, and nearly identical replacement for performSelector
without having to much about with objc_msgSend()
.
Though, I have no idea if there is an analog available on iOS.