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?

15voto

c roald Punkte 1894

Die Antwort von Matt Galloway zu diesem Thread erklärt das Warum:

Betrachten Sie folgendes:

id anotherObject1 = [someObject performSelector:@selector(copy)];
id anotherObject2 = [someObject performSelector:@selector(giveMeAnotherNonRetainedObject)];

Jetzt, wie kann ARC wissen, dass das erste einen Rückgabewert mit einem Zählwert von 1 zurückgibt, aber das zweite gibt ein Objekt zurück, das autorelease ist?

Es scheint, dass es im Allgemeinen sicher ist, die Warnung zu unterdrücken, wenn Sie den Rückgabewert ignorieren. Ich bin mir nicht sicher, was die beste Praxis ist, wenn Sie wirklich ein behaltenes Objekt von performSelector erhalten müssen - außer "das nicht zu tun".

14voto

Pavel Osipov Punkte 2027

@c-road bietet den richtigen Link mit einer Problemdefinition hier. Im Folgenden sehen Sie mein Beispiel, bei dem performSelector zu einem Memory-Leak führt.

@interface Dummy : NSObject 
@end

@implementation Dummy

- (id)copyWithZone:(NSZone *)zone {
  return [[Dummy alloc] init];
}

- (id)clone {
  return [[Dummy alloc] init];
}

@end

void CopyDummy(Dummy *dummy) {
  __unused Dummy *dummyClone = [dummy copy];
}

void CloneDummy(Dummy *dummy) {
  __unused Dummy *dummyClone = [dummy clone];
}

void CopyDummyWithLeak(Dummy *dummy, SEL copySelector) {
  __unused Dummy *dummyClone = [dummy performSelector:copySelector];
}

void CloneDummyWithoutLeak(Dummy *dummy, SEL cloneSelector) {
  __unused Dummy *dummyClone = [dummy performSelector:cloneSelector];
}

int main(int argc, const char * argv[]) {
  @autoreleasepool {
    Dummy *dummy = [[Dummy alloc] init];
    for (;;) { @autoreleasepool {
      //CopyDummy(dummy);
      //CloneDummy(dummy);
      //CloneDummyWithoutLeak(dummy, @selector(clone));
      CopyDummyWithLeak(dummy, @selector(copy));
      [NSThread sleepForTimeInterval:1];
    }} 
  }
  return 0;
}

Die einzige Methode, die in meinem Beispiel einen Memory-Leak verursacht, ist CopyDummyWithLeak. Der Grund dafür ist, dass ARC nicht weiß, dass copySelector ein behaltenes Objekt zurückgibt.

Wenn Sie das Memory-Leak-Tool ausführen, können Sie das folgende Bild sehen: enter image description here ... und es gibt in keinem anderen Fall Memory-Leaks: enter image description here

6voto

Ben Flynn Punkte 17854

Um Scott Thompsons Makro allgemeiner zu gestalten:

// String-Expander
#define MY_STRX(X) #X
#define MY_STR(X) MY_STRX(X)

#define MYSilenceWarning(FLAG, MACRO) \
_Pragma("clang diagnostic push") \
_Pragma(MY_STR(clang diagnostic ignored MY_STR(FLAG))) \
MACRO \
_Pragma("clang diagnostic pop")

Verwenden Sie es dann so:

MYSilenceWarning(-Warc-performSelector-leaks,
[_target performSelector:_action withObject:self];
                )

6voto

SwiftArchitect Punkte 45044

Warnungen nicht unterdrücken!

Es gibt nicht weniger als 12 alternative Lösungen, um mit dem Compiler herumzuspielen.
Während du vielleicht klug bist, wenn du die erste Implementierung machst, kann kaum ein Ingenieur auf der Erde deinen Fußstapfen folgen, und dieser Code wird schließlich brechen.

Sichere Wege:

All diese Lösungen werden funktionieren, mit mehr oder weniger Abweichung von deiner ursprünglichen Absicht. Gehe davon aus, dass param bei Bedarf nil sein kann:

Sicherer Weg, gleiche konzeptionelle Verhalten:

// GUT
[_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]];

Sicherer Weg, leicht unterschiedliches Verhalten:

(Siehe diese Antwort)
Verwende jeden Thread anstelle von [NSThread mainThread].

// GUT
[_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]];

Gefährliche Wege

Erfordert eine Art von Compiler-Unterdrückung, die sicher brechen wird. Beachte, dass es zurzeit in Swift gebrochen ist.

// AUF EIGENES RISIKO
[_controller performSelector:selector];
[_controller performSelector:selector withObject:anArgument];
[_controller performSelector:selector withObject:anArgument withObject:nil];

4voto

honus Punkte 799

Weil Sie ARC verwenden, müssen Sie iOS 4.0 oder höher verwenden. Dies bedeutet, dass Sie Blöcke verwenden könnten. Wenn Sie anstelle des Merkens des Selektors, der ausgeführt werden soll, stattdessen einen Block aufnehmen, könnte ARC besser verfolgen, was tatsächlich geschieht, und Sie müssten nicht das Risiko eingehen, versehentlich einen Speicherleck einzuführen.

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