performSelector may cause a leak because its selector is unknown

后端 未结 19 2191
小蘑菇
小蘑菇 2020-11-22 01:54

I\'m getting the following warning by the ARC compiler:

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

Here\'s what

相关标签:
19条回答
  • 2020-11-22 02:40

    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];
        }
    
    0 讨论(0)
  • 2020-11-22 02:43

    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.

    0 讨论(0)
  • 2020-11-22 02:43

    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".

    0 讨论(0)
  • 2020-11-22 02:44

    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.

    0 讨论(0)
  • 2020-11-22 02:45

    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
    
    0 讨论(0)
  • 2020-11-22 02:45

    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.

    0 讨论(0)
提交回复
热议问题