wake-up-neo.com

performSelector kann ein Leck verursachen, da sein Selektor unbekannt ist

Ich erhalte die folgende Warnung vom ARC-Compiler:

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

Folgendes mache ich:

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

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

1242
Eduardo Scoz

Lösung

Der Compiler warnt aus einem bestimmten Grund davor. Es kommt sehr selten vor, dass diese Warnung einfach ignoriert wird, und es ist einfach, sie zu umgehen. Hier ist wie:

if (!_controller) { return; }
SEL selector = NSSelectorFromString(@"someMethod");
IMP imp = [_controller methodForSelector:selector];
void (*func)(id, SEL) = (void *)imp;
func(_controller, selector);

Oder knapper (obwohl schwer zu lesen und ohne Wache):

SEL selector = NSSelectorFromString(@"someMethod");
((void (*)(id, SEL))[_controller methodForSelector:selector])(_controller, selector);

Erläuterung

Hier fragen Sie den Controller nach dem C-Funktionszeiger für die Methode, die dem Controller entspricht. Alle NSObject antworten auf methodForSelector:, Sie können jedoch auch class_getMethodImplementation in der Objective-C-Laufzeit verwenden (nützlich, wenn Sie nur über eine Protokollreferenz verfügen, z. B. id<SomeProto>). Diese Funktionszeiger heißen IMPs und sind einfache typedefed-Funktionszeiger (id (*IMP)(id, SEL, ...)).1. Dies kann nahe an der tatsächlichen Methodensignatur der Methode liegen, stimmt jedoch nicht immer genau überein.

Sobald Sie den IMP haben, müssen Sie ihn in einen Funktionszeiger umwandeln, der alle Details enthält, die ARC benötigt (einschließlich der beiden impliziten versteckten Argumente self und _cmd jedes Objective- C Methodenaufruf). Dies wird in der dritten Zeile behandelt (der (void *) auf der rechten Seite teilt dem Compiler einfach mit, dass Sie wissen, was Sie tun, und dass keine Warnung generiert werden soll, da die Zeigertypen nicht übereinstimmen).

Zuletzt rufen Sie den Funktionszeiger auf2.

Komplexes Beispiel

Wenn der Selektor Argumente akzeptiert oder einen Wert zurückgibt, müssen Sie einige Dinge ändern:

SEL selector = NSSelectorFromString(@"processRegion:ofView:");
IMP imp = [_controller methodForSelector:selector];
CGRect (*func)(id, SEL, CGRect, UIView *) = (void *)imp;
CGRect result = _controller ?
  func(_controller, selector, someRect, someView) : CGRectZero;

Begründung für die Warnung

Der Grund für diese Warnung ist, dass die Laufzeit bei ARC wissen muss, was mit dem Ergebnis der aufgerufenen Methode geschehen soll. Das Ergebnis kann alles Mögliche sein: void, int, char, NSString *, id usw. Normalerweise bezieht ARC diese Informationen aus dem Header des Objekttyps du arbeitest mit.3

Es gibt wirklich nur 4 Dinge, die ARC für den Rückgabewert berücksichtigen würde:4

  1. Nicht-Objekttypen ignorieren (void, int usw.)
  2. Objektwert beibehalten, dann freigeben, wenn er nicht mehr verwendet wird (Standardannahme)
  3. Neue Objektwerte freigeben, wenn sie nicht mehr verwendet werden (Methoden in der init/copy-Familie oder mit ns_returns_retained belegt)
  4. Tun Sie nichts und gehen Sie davon aus, dass der zurückgegebene Objektwert im lokalen Bereich gültig ist (bis der innerste Release-Pool geleert ist und ns_returns_autoreleased zugewiesen wurde).

Der Aufruf von methodForSelector: geht davon aus, dass der Rückgabewert der aufgerufenen Methode ein Objekt ist, es jedoch nicht beibehält/freigibt. Sie könnten also ein Leck erzeugen, wenn Ihr Objekt wie in # 3 oben freigegeben werden soll (das heißt, die Methode, die Sie aufrufen, gibt ein neues Objekt zurück).

Für Selektoren, die Sie versuchen, diese Rückgabe void oder andere Nicht-Objekte aufzurufen, können Sie Compiler-Funktionen aktivieren, um die Warnung zu ignorieren, dies kann jedoch gefährlich sein. Ich habe gesehen, wie Clang einige Iterationen durchlief, wie er Rückgabewerte handhabt, die nicht lokalen Variablen zugewiesen sind. Bei aktiviertem ARC kann der von methodForSelector: zurückgegebene Objektwert nicht beibehalten und freigegeben werden, obwohl Sie ihn nicht verwenden möchten. Aus der Sicht des Compilers ist es schließlich ein Objekt. Das bedeutet, dass, wenn die aufgerufene Methode someMethod ein Nicht-Objekt (einschließlich void) zurückgibt, ein Müllzeigerwert beibehalten/freigegeben werden und abstürzen kann.

Zusätzliche Argumente

Eine Überlegung ist, dass dies die gleiche Warnung ist, die bei performSelector:withObject: auftritt, und Sie könnten auf ähnliche Probleme stoßen, wenn Sie nicht deklarieren, wie diese Methode Parameter verbraucht. ARC erlaubt das Deklarieren von konsumierte Parameter , und wenn die Methode den Parameter konsumiert, werden Sie wahrscheinlich irgendwann eine Nachricht an einen Zombie senden und abstürzen. Es gibt Möglichkeiten, dies mit Bridged Casting zu umgehen, aber es wäre wirklich besser, einfach die oben beschriebene Methode für IMP und Funktionszeiger zu verwenden. Da verbrauchte Parameter selten ein Problem darstellen, ist es unwahrscheinlich, dass dies auftritt.

Statische Selektoren

Interessanterweise beschwert sich der Compiler nicht über statisch deklarierte Selektoren:

[_controller performSelector:@selector(someMethod)];

Der Grund dafür ist, dass der Compiler tatsächlich alle Informationen über den Selektor und das Objekt während der Kompilierung aufzeichnen kann. Es müssen keine Annahmen über irgendetwas gemacht werden. (Ich habe dies vor einem Jahr überprüft, indem ich mir die Quelle angesehen habe, habe aber momentan keine Referenz.)

Unterdrückung

Bei dem Versuch, an eine Situation zu denken, in der die Unterdrückung dieser Warnung notwendig und ein gutes Code-Design erforderlich wäre, komme ich leer aus. Bitte teilen Sie jemandem mit, wenn er eine Erfahrung gemacht hat, bei der diese Warnung stummgeschaltet werden musste (und die oben genannten Dinge nicht richtig handhaben).

Mehr

Es ist möglich, ein NSMethodInvocation zu erstellen, um dies ebenfalls zu handhaben, aber dies erfordert viel mehr Eingaben und ist auch langsamer. Es gibt also wenig Grund, dies zu tun.

Geschichte

Als die Methodenfamilie performSelector: zum ersten Mal zu Objective-C hinzugefügt wurde, war ARC nicht vorhanden. Beim Erstellen von ARC entschied Apple, dass für diese Methoden eine Warnung generiert werden sollte, um die Entwickler dazu anzuhalten, andere Methoden zu verwenden, um explizit zu definieren, wie der Speicher beim Senden beliebiger Nachrichten über einen benannten Selektor behandelt werden soll. In Objective-C können Entwickler dazu C-Darstellungen für rohe Funktionszeiger verwenden.

Mit der Einführung von Swift hat Apple dokumentiert die Methodenfamilie performSelector: als "inhärent unsicher" eingestuft und steht Swift nicht zur Verfügung.

Im Laufe der Zeit haben wir diesen Fortschritt gesehen:

  1. Frühere Versionen von Objective-C ermöglichen performSelector: (manuelle Speicherverwaltung)
  2. Objective-C mit ARC warnt vor der Verwendung von performSelector:
  3. Swift hat keinen Zugriff auf performSelector: und dokumentiert diese Methoden als "inhärent unsicher"

Die Idee, Nachrichten basierend auf einem benannten Selektor zu senden, ist jedoch keine "inhärent unsichere" Funktion. Diese Idee wird seit langem erfolgreich in Objective-C und vielen anderen Programmiersprachen eingesetzt.


1 Alle Objective-C-Methoden haben zwei versteckte Argumente, self und _cmd, die implizit hinzugefügt werden, wenn Sie eine Methode aufrufen.

2 Das Aufrufen einer NULL -Funktion ist in C nicht sicher. Der Wächter, mit dem das Vorhandensein des Controllers überprüft wird, stellt sicher, dass wir ein Objekt haben. Wir wissen daher, dass wir von methodForSelector: einen IMP bekommen (obwohl es _objc_msgForward sein kann, Eintrag in das Nachrichtenweiterleitungssystem). Grundsätzlich wissen wir, dass wir mit der Wache eine Funktion haben, die wir anrufen können.

3 Tatsächlich ist es möglich, dass die falschen Informationen angezeigt werden, wenn Sie Objekte als id deklarieren und nicht alle Header importieren. Es kann zu Abstürzen im Code kommen, die der Compiler für in Ordnung hält. Dies ist sehr selten, könnte aber passieren. Normalerweise erhalten Sie nur eine Warnung, dass nicht bekannt ist, aus welchen beiden Methodensignaturen Sie auswählen sollen.

4 Weitere Informationen finden Sie in der ARC-Referenz zu beibehaltene Rückgabewerte und nicht gespeicherte Rückgabewerte .

1193
wbyoung

Im LLVM 3.0-Compiler in Xcode 4.2 können Sie die Warnung wie folgt unterdrücken:

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

Wenn der Fehler an mehreren Stellen auftritt und Sie das C-Makrosystem zum Ausblenden der Pragmas verwenden möchten, können Sie ein Makro definieren, um die Unterdrückung der Warnung zu vereinfachen:

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

Sie können das Makro folgendermaßen verwenden:

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

Wenn Sie das Ergebnis der durchgeführten Meldung benötigen, können Sie dies tun:

id result;
SuppressPerformSelectorLeakWarning(
    result = [_target performSelector:_action withObject:self]
);
1178
Scott Thompson

Meine Vermutung lautet: Da der Selektor dem Compiler unbekannt ist, kann ARC keine ordnungsgemäße Speicherverwaltung erzwingen.

In der Tat gibt es Zeiten, in denen die Speicherverwaltung durch eine bestimmte Konvention an den Namen der Methode gebunden ist. Insbesondere denke ich an Convenience-Konstruktoren versus make Methoden; die erstere gibt ein automatisch freigegebenes Objekt vereinbarungsgemäß zurück; letzteres ein zurückbehaltener Gegenstand. Die Konvention basiert auf den Namen des Selektors. Wenn der Compiler den Selektor nicht kennt, kann er die richtige Speicherverwaltungsregel nicht durchsetzen.

Wenn dies korrekt ist, können Sie Ihren Code sicher verwenden, sofern Sie sicherstellen, dass die Speicherverwaltung in Ordnung ist (z. B., dass Ihre Methoden keine von ihnen zugewiesenen Objekte zurückgeben).

208
sergio

Fügen Sie in Ihrem Projekt Build Settings unter Other Warning Flags (WARNING_CFLAGS) hinzu
-Wno-arc-performSelector-leaks

Stellen Sie jetzt nur sicher, dass der von Ihnen aufgerufene Selektor nicht dazu führt, dass Ihr Objekt beibehalten oder kopiert wird.

120
0xced

Um dieses Problem zu umgehen, können Sie die Laufzeitumgebung verwenden, bis der Compiler das Überschreiben der Warnung zulässt

objc_msgSend(_controller, NSSelectorFromString(@"someMethod"));

anstatt von

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

Du musst

#import <objc/message.h>
111
jluckyiv

Fügen Sie ein #pragma wie folgt hinzu, um den Fehler nur in der Datei mit dem Perform-Selektor zu ignorieren:

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

Dies würde die Warnung in dieser Zeile ignorieren, sie jedoch für den Rest Ihres Projekts zulassen.

88
Barlow Tucker

Merkwürdig, aber wahr: Wenn akzeptabel (d. H. Das Ergebnis ist ungültig und es macht Ihnen nichts aus, den Runloop-Zyklus einmal zuzulassen), fügen Sie eine Verzögerung hinzu, auch wenn diese Null ist:

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

Dadurch wird die Warnung entfernt, vermutlich, weil der Compiler dadurch beruhigt wird, dass kein Objekt zurückgegeben und auf irgendeine Weise falsch verwaltet werden kann.

68
matt

Hier ist ein aktualisiertes Makro basierend auf der oben gegebenen Antwort. Dieser sollte es Ihnen ermöglichen, Ihren Code auch mit einer return-Anweisung zu umbrechen.

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

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 dies im Gegensatz zu performSelector bei jeder Methode funktioniert.

31
Benedict Cohen

Nun, viele Antworten hier, aber da dies ein wenig anders ist, kombiniere ich ein paar Antworten, von denen ich dachte, dass ich sie einfügen würde. Ich verwende eine NSObject-Kategorie, die prüft, ob der Selektor ungültig ist und den Compiler unterdrückt Warnung.

#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
20
Chris Prince

Der Nachwelt zuliebe habe ich beschlossen, meinen Hut in den Ring zu werfen :)

Vor kurzem habe ich immer mehr Umstrukturierungen außerhalb des target/selector -Paradigmas zugunsten von Dingen wie Protokollen, Blöcken usw. erlebt. Es gibt jedoch einen Ersatz für performSelector das ich jetzt schon ein paar mal benutzt habe:

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

Dies scheint ein sauberer, ARC-sicherer und nahezu identischer Ersatz für performSelector zu sein, ohne sich mit objc_msgSend() zu beschäftigen.

Ich habe allerdings keine Ahnung, ob es ein Analogon für iOS gibt.

16
Patrick Perini

Die Antwort von Matt Galloway auf dieser Thread erklärt das Warum:

Folgendes berücksichtigen:

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

Woher kann ARC nun wissen, dass der erste ein Objekt mit einer Beibehaltungsanzahl von 1 zurückgibt, der zweite jedoch ein Objekt, das automatisch freigegeben wurde?

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

15
c roald

@ c-road liefert den richtigen Link mit Problembeschreibung hier . Unten sehen Sie mein Beispiel, wenn performSelector einen Speicherverlust verursacht.

@interface Dummy : NSObject <NSCopying>
@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 Speicherverlust verursacht, ist CopyDummyWithLeak. Der Grund dafür ist, dass ARC nicht weiß, dass copySelector ein beibehaltenes Objekt zurückgibt.

Wenn Sie das Memory Leak Tool ausführen, wird das folgende Bild angezeigt: enter image description here ... und in keinem anderen Fall gibt es Speicherlecks: enter image description here

14
Pavel Osipov

Um das Makro von Scott Thompson 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")

Dann benutze es so:

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

Warnungen nicht unterdrücken!

Es gibt nicht weniger als 12 alternative Lösungen zum Basteln mit dem Compiler.
Während Sie zur Zeit der ersten Implementierung clever sind, können nur wenige Ingenieure auf der Erde Ihren Schritten folgen, und dieser Code wird irgendwann brechen.

Sichere Routen:

Alle diese Lösungen funktionieren mit einem gewissen Grad an Abweichung von Ihrer ursprünglichen Absicht. Angenommen, param kann nil sein, wenn Sie dies wünschen:

Sichere Route, gleiches konzeptionelles Verhalten:

// 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]];

Sichere Route, etwas anderes Verhalten:

(Siehe this response)
Verwenden Sie einen beliebigen Thread anstelle von [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]];

Gefährliche Routen

Erfordert eine Art Compiler-Stummschaltung, die zwangsläufig unterbrochen wird. Beachten Sie, dass es derzeit did break in Swift.

// AT YOUR OWN RISK
[_controller performSelector:selector];
[_controller performSelector:selector withObject:anArgument];
[_controller performSelector:selector withObject:anArgument withObject:nil];
6
SwiftArchitect

Da Sie ARC verwenden, müssen Sie iOS 4.0 oder höher verwenden. Das heißt, Sie könnten Blöcke verwenden. Wenn ARC sich nicht mehr an den Selektor erinnert, den Sie ausgeführt haben, sondern einen Block nimmt, kann es besser verfolgen, was tatsächlich vor sich geht, und Sie müssen nicht das Risiko eingehen, versehentlich einen Speicherverlust zu verursachen.

4
honus

Anstatt den Block-Ansatz zu verwenden, gab es einige Probleme:

    IMP imp = [_controller methodForSelector:selector];
    void (*func)(id, SEL) = (void *)imp;

Ich werde NSInvocation folgendermaßen verwenden:

    -(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];
    }
2
supersabbath

Wenn Sie keine Argumente übergeben müssen, können Sie dies einfach umgehen, indem Sie valueForKeyPath verwenden. Dies ist sogar für ein Class Objekt möglich.

NSString *colorName = @"brightPinkColor";
id uicolor = [UIColor class];
if ([uicolor respondsToSelector:NSSelectorFromString(colorName)]){
    UIColor *brightPink = [uicolor valueForKeyPath:colorName];
    ...
}
1
arsenius