performSelector may cause a leak because its selector is unknown

后端 未结 19 2190
小蘑菇
小蘑菇 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:28

    You could also use a protocol here. So, create a protocol like so:

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

    In your class that needs to call your selector, you then have a @property.

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

    When you need to call @selector(doSomethingWithObject:) in an instance of MyObject, do this:

    [self.source doSomethingWithObject:object];
    
    0 讨论(0)
  • 2020-11-22 02:29

    Here is an updated macro based on the answer given above. This one should allow you to wrap your code even with a return statement.

    #define SUPPRESS_PERFORM_SELECTOR_LEAK_WARNING(code)                        \
        _Pragma("clang diagnostic push")                                        \
        _Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"")     \
        code;                                                                   \
        _Pragma("clang diagnostic pop")                                         \
    
    
    SUPPRESS_PERFORM_SELECTOR_LEAK_WARNING(
        return [_target performSelector:_action withObject:self]
    );
    
    0 讨论(0)
  • 2020-11-22 02:32

    To ignore the error only in the file with the perform selector, add a #pragma as follows:

    #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    

    This would ignore the warning on this line, but still allow it throughout the rest of your project.

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

    Do not suppress warnings!

    There are no less than 12 alternative solutions to tinkering with the compiler.
    While you are being clever at the time the first implementation, few engineer on Earth can follow your footsteps, and this code will eventually break.

    Safe Routes:

    All these solutions will work, with some degree of of variation from your original intent. Assume that param can be nil if you so desire:

    Safe route, same conceptual behavior:

    // GREAT
    [_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:YES];
    [_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:YES modes:@[(__bridge NSString *)kCFRunLoopDefaultMode]];
    
    [_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:YES];
    [_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:YES modes:@[(__bridge NSString *)kCFRunLoopDefaultMode]];
    

    Safe route, slightly different behavior:

    (See this response)
    Use any thread in lieu of [NSThread mainThread].

    // GOOD
    [_controller performSelector:selector withObject:anArgument afterDelay:0];
    [_controller performSelector:selector withObject:anArgument afterDelay:0 inModes:@[(__bridge NSString *)kCFRunLoopDefaultMode]];
    
    [_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:NO];
    [_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:NO];
    [_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:NO modes:@[(__bridge NSString *)kCFRunLoopDefaultMode]];
    
    [_controller performSelectorInBackground:selector withObject:anArgument];
    
    [_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:NO];
    [_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:NO modes:@[(__bridge NSString *)kCFRunLoopDefaultMode]];
    

    Dangerous Routes

    Requires some kind of compiler silencing, which is bound to break. Note that at present time, it did break in Swift.

    // AT YOUR OWN RISK
    [_controller performSelector:selector];
    [_controller performSelector:selector withObject:anArgument];
    [_controller performSelector:selector withObject:anArgument withObject:nil];
    
    0 讨论(0)
  • 2020-11-22 02:36

    If you don't need to pass any arguments an easy workaround is to use valueForKeyPath. This is even possible on a Class object.

    NSString *colorName = @"brightPinkColor";
    id uicolor = [UIColor class];
    if ([uicolor respondsToSelector:NSSelectorFromString(colorName)]){
        UIColor *brightPink = [uicolor valueForKeyPath:colorName];
        ...
    }
    
    0 讨论(0)
  • 2020-11-22 02:37

    In the LLVM 3.0 compiler in Xcode 4.2 you can suppress the warning as follows:

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

    If you're getting the error in several places, and want to use the C macro system to hide the pragmas, you can define a macro to make it easier to suppress the warning:

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

    You can use the macro like this:

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

    If you need the result of the performed message, you can do this:

    id result;
    SuppressPerformSelectorLeakWarning(
        result = [_target performSelector:_action withObject:self]
    );
    
    0 讨论(0)
提交回复
热议问题