1295 Stimmen

PerformSelector kann ein Leck verursachen, weil sein Selektor unbekannt ist.

Ich erhalte die folgende Warnung vom ARC-Compiler:

"performSelector kann ein Leck verursachen, weil sein Selektor unbekannt ist".

Das ist, was ich tue:

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

Warum erhalte ich diese Warnung? Ich verstehe, dass der Compiler nicht überprüfen kann, ob der Selektor existiert oder nicht, aber warum würde das ein Leck verursachen? Und wie kann ich meinen Code ändern, damit ich diese Warnung nicht mehr erhalte?

70voto

matt Punkte 481428

Seltsam aber wahr: Wenn akzeptabel (d.h. das Ergebnis ist void und es macht nichts aus, wenn der Runloop einmal durchläuft), fügen Sie eine Verzögerung hinzu, auch wenn diese Null beträgt:

[_controller performSelector:NSSelectorFromString(@"someMethod")
    withObject:nil
    afterDelay:0];

Dies entfernt die Warnung, vermutlich, weil es den Compiler beruhigt, dass kein Objekt zurückgegeben und irgendwie fehlerhaft verwaltet werden kann.

34voto

syvex Punkte 7236

Hier ist ein aktualisiertes Makro basierend auf der oben gegebenen Antwort. Dieses sollte es Ihnen ermöglichen, Ihren Code auch mit einer return-Anweisung einzuschließen.

#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]
);

31voto

Benedict Cohen Punkte 11742

Dieser Code beinhaltet keine Compiler-Flags oder direkte Laufzeitaufrufe:

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

NSInvocation ermöglicht das Setzen mehrerer Argumente, so dass im Gegensatz zu performSelector dies bei jedem Methode funktionieren wird.

21voto

Chris Prince Punkte 6870

Nun, hier sind viele Antworten, aber da dies ein wenig anders ist, kombiniere ich ein paar Antworten, die ich dachte, dass ich sie einfügen würde. Ich benutze eine NSObject-Kategorie, die überprüft, ob der Selektor void zurückgibt, und unterdrückt auch die Compilerwarnung.

#import 
#import 
#import "Debug.h" // nicht gegeben; nur eine Behauptung

@interface NSObject (Extras)

// Verfolgen Sie die Regel, dass der verwendete Selektor void zurückgeben muss.
- (void) performVoidReturnSelector:(SEL)aSelector withObject:(id)object;
- (void) performVoidReturnSelector:(SEL)aSelector;

@end

@implementation NSObject (Extras)

// Anscheinend gibt die reguläre performSelect eine Warnung zur Kompilierzeit aus, weil das System den Rückgabetyp nicht kennt. Ich werde (a) sicherstellen, dass der Rückgabetyp void ist und (b) diese Warnung deaktivieren
// Siehe http://stackoverflow.com/questions/7017281/performselector-may-cause-a-leak-because-its-selector-is-unknown

- (void) checkSelector:(SEL)aSelector {
    // Siehe 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:@"%@ war nicht 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"
    // Da der Selektor (aSelector) void zurückgibt, ergibt es keinen Sinn, zu versuchen, das Rückgabeergebnis von performSelector zu erhalten. Tatsächlich stürzt die App ab, wenn wir es tun.
    [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

16voto

Patrick Perini Punkte 22351

Zur Wahrung der Ewigkeit habe ich beschlossen, meinen Hut in den Ring zu werfen :)

In letzter Zeit habe ich immer mehr Umstrukturierungen weg vom Ziel/Selektor-Paradigma gesehen, zugunsten von Dingen wie Protokollen, Blöcken usw. Es gibt jedoch einen sauberen Ersatz für performSelector, den ich jetzt schon ein paar Mal verwendet habe:

[NSApp sendAction: NSSelectorFromString(@"someMethod") to: _controller from: nil];

Diese scheinen ein sauberer, ARC-sicherer und nahezu identischer Ersatz für performSelector zu sein, ohne viel mit objc_msgSend() herumfummeln zu müssen.

Ich habe jedoch keine Ahnung, ob auf iOS ein Analog verfügbar ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X