performSelector może spowodować wyciek, ponieważ jego Selektor jest nieznany

Otrzymuję następujące ostrzeżenie przez kompilator ARC:

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

Oto co robię:

[_controller performSelector:NSSelectorFromString(@"someMethod")];
Dlaczego dostaję to Ostrzeżenie? Rozumiem, że kompilator nie może sprawdzić, czy selektor istnieje, ale dlaczego miałoby to spowodować wyciek? I jak Mogę zmienić mój kod, aby nie dostać to Ostrzeżenie więcej?
Author: James Webster, 2011-08-11

19 answers

Rozwiązanie

Kompilator ostrzega o tym nie bez powodu. To bardzo rzadkie, że to Ostrzeżenie powinno być po prostu ignorowane, a to jest łatwe do obejścia. Oto jak:

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

Lub bardziej treściwie (choć trudno czytać i bez straży):

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

Wyjaśnienie

Chodzi o to, że prosisz kontroler o wskaźnik funkcji C dla metody odpowiadającej kontrolerowi. Wszystkie NSObjectodpowiadają na methodForSelector:, ale można również użyć class_getMethodImplementation w Objective - C runtime (przydatne, jeśli masz tylko odniesienie do protokołu, jak id<SomeProto>). Te wskaźniki funkcji nazywane są IMP s i są prostymi wskaźnikami funkcji typedef ed(id (*IMP)(id, SEL, ...))1. Może to być zbliżone do rzeczywistej sygnatury metody, ale nie zawsze będzie dokładnie pasować.

Gdy masz IMP, musisz wrzucić go do wskaźnika funkcji, który zawiera wszystkie szczegóły potrzebne ARC (w tym dwa ukryte argumenty self i _cmd każdego celu-C wywołanie metody). Jest to obsługiwane w trzeciej linii ((void *) po prawej stronie po prostu mówi kompilatorowi, że wiesz, co robisz i nie generuje ostrzeżenia, ponieważ typy wskaźników nie pasują).

Na koniec wywołujesz wskaźnik funkcji2.

Przykład Złożony

Gdy selektor pobierze argumenty lub zwróci wartość, będziesz musiał nieco zmienić:

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;

Uzasadnienie Ostrzeżenia

Powodem tego ostrzeżenia jest to, że w przypadku ARC środowisko wykonawcze musi wiedzieć, co zrobić z wynikiem wywołanej metody. Rezultatem może być wszystko: void, int, char, NSString *, id, itd. ARC zwykle pobiera te informacje z nagłówka typu obiektu, z którym pracujesz.3

Są naprawdę tylko 4 rzeczy, które Arc rozważyłby dla wartości zwracanej:4

  1. Ignoruj typy inne niż obiektowe (void, int, etc)
  2. zachowuje wartość obiektu, a następnie zwalnia, gdy jest nie jest już używany (standardowe założenie)
  3. zwalnia nowe wartości obiektów, gdy nie są już używane (metody w init/ copy rodzina lub przypisane do ns_returns_retained)
  4. nie rób nic i zakładaj, że zwrócona wartość obiektu będzie ważna w lokalnym zasięgu (dopóki wewnętrzna Pula wydań nie zostanie opróżniona, przypisana do ns_returns_autoreleased)

Wywołanie methodForSelector: zakłada, że zwracana wartość metody jest obiektem, ale nie zachowuje/nie zwalnia jej. Więc może skończyć się tworzeniem przecieku, jeśli twój obiekt powinien zostać zwolniony tak jak w #3 powyżej (tzn. metoda, którą wywołujesz zwraca nowy obiekt).

W przypadku selektorów, które próbujesz wywołać, zwróć void lub innych nie-obiektów, możesz włączyć funkcje kompilatora, aby ignorowały Ostrzeżenie, ale może to być niebezpieczne. Widziałem Clang przejść przez kilka iteracji jak obsługuje wartości zwracane, które nie są przypisane do zmiennych lokalnych. Nie ma powodu, aby z włączonym ARC nie mógł zachować i zwolnić wartości obiektu, która jest / align = "left" / Z punktu widzenia kompilatora jest to jednak obiekt. Oznacza to, że jeśli wywołana metoda someMethod zwraca obiekt inny niż obiekt (w tym void), może to spowodować zatrzymanie/zwolnienie wskaźnika śmieci i awarię.

Dodatkowe Argumenty

Należy wziąć pod uwagę, że to samo ostrzeżenie wystąpi z performSelector:withObject: i możesz napotkać podobne problemy z nie deklarowaniem, jak ta metoda zużywa parametry. ARC pozwala na deklarowanie zużytych parametrów, a jeśli metoda zużyje parametr, prawdopodobnie w końcu wyślesz wiadomość do zombie i zawiesisz działanie. Istnieją sposoby obejścia tego problemu z odlewaniem pomostowym, ale naprawdę lepiej byłoby po prostu użyć metody IMP i wskaźnika funkcji powyżej. Ponieważ zużyte parametry rzadko stanowią problem, prawdopodobnie nie pojawi się to.

Selektory Statyczne

Co ciekawe, kompilator nie będzie narzekał o selektorach deklarowanych statycznie:

[_controller performSelector:@selector(someMethod)];

Powodem tego jest to, że kompilator jest w stanie podczas kompilacji zapisać wszystkie informacje o selektorze i obiekcie. Nie musi niczego zakładać. (Sprawdziłem to rok temu, patrząc na źródło, ale nie mam teraz odniesienia.)

Tłumienie

Próbując myśleć o sytuacji, w której tłumienie tego ostrzeżenia byłoby konieczne i dobry kod design, nic nie widzę. Niech ktoś się podzieli, jeśli miał doświadczenie, w którym wyciszenie tego ostrzeżenia było konieczne (a powyższe nie obsługuje rzeczy prawidłowo).

Więcej

Możliwe jest zbudowanie NSMethodInvocation, aby poradzić sobie z tym, ale wymaga to znacznie więcej pisania i jest również wolniejsze, więc nie ma powodu, aby to zrobić.

Historia

Kiedy rodzina metod została po raz pierwszy dodana do Objective-C, ARC nie istniał. Podczas tworzenia łuku, Apple zdecydowało, że Ostrzeżenie powinno zostać wygenerowane dla tych metod jako sposób na poprowadzenie programistów do korzystania z innych środków, aby wyraźnie określić, jak należy obsługiwać pamięć podczas wysyłania dowolnych wiadomości za pośrednictwem nazwanego selektora. W Objective-C, deweloperzy są w stanie to zrobić, używając stylu C rzuca na surowych wskaźników funkcji.

[48]}wraz z wprowadzeniem Swift, Apple udokumentował performSelector: rodzinę metod jako "z natury niebezpieczne" i nie są one dostępne dla Swift.

Z biegiem czasu, widzieliśmy ten postęp:

    Obiektywizm (ang. Objective-C) - System zarządzania pamięcią (manual memory management), działający w oparciu o obiektywizm (ang. Manual memory management).]}
  1. Objective-C Z ARC ostrzega przed użyciem performSelector:
  2. Nie jest to jednak możliwe w przypadku, gdy nie jest to możliwe.]}

Idea wysyłania wiadomości na podstawie nazwanego selektora nie jest jednak funkcją "z natury niebezpieczną". Pomysł ten od dawna z powodzeniem stosowany jest w Objective-C oraz wiele innych języków programowania.


1 wszystkie metody Objective-C mają dwa ukryte argumenty, self i _cmd, które są domyślnie dodawane podczas wywoływania metody.

2 wywołanie funkcji NULL nie jest bezpieczne w C. strażnik używany do sprawdzania obecności kontrolera zapewnia, że mamy obiekt. Wiemy zatem, że otrzymamy IMP z methodForSelector: (choć może to być _objc_msgForward, wejście do systemu przekazywania wiadomości). W zasadzie, z strażnikiem na miejscu, wiemy, że mamy funkcję do wywołania.

3 W rzeczywistości jest możliwe, aby uzyskać złe informacje, jeśli zadeklarujesz obiekty jako id i nie importujesz wszystkich nagłówków. Możesz skończyć z awariami w kodzie, który kompilator uważa za dobry. Jest to bardzo rzadkie, ale może się zdarzyć. Zazwyczaj po prostu otrzymasz ostrzeżenie, że nie wie, który z dwóch podpisów metod wybrać.

4 Zobacz referencje ARC na zachowane wartości zwracane i nieprzetworzone wartości zwracane aby uzyskać więcej szczegółów.

 1147
Author: wbyoung,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-12 03:17:54

W kompilatorze LLVM 3.0 w Xcode 4.2 można wyłączyć ostrzeżenie w następujący sposób:

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

Jeśli otrzymujesz błąd w kilku miejscach i chcesz użyć systemu makr C do ukrycia pragmatów, możesz zdefiniować makro, aby łatwiej tłumić Ostrzeżenie:

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

Możesz użyć makra w następujący sposób:

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

Jeśli potrzebujesz wyniku wykonanej wiadomości, możesz to zrobić:

id result;
SuppressPerformSelectorLeakWarning(
    result = [_target performSelector:_action withObject:self]
);
 1173
Author: Scott Thompson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-24 22:18:32

Moje przypuszczenie jest takie: ponieważ Selektor jest nieznany kompilatorowi, ARC nie może wymusić właściwego zarządzania pamięcią.

W rzeczywistości są chwile, kiedy zarządzanie pamięcią jest powiązane z nazwą metody przez określoną konwencję. W szczególności, myślę o convenience constructors versus make methods; pierwszy zwraca przez Konwencję obiekt z autoreleased, drugi obiekt zachowany. Konwencja opiera się na nazwach selektora, więc jeśli kompilator nie zna selektora, wtedy nie może wymusić odpowiedniej reguły zarządzania pamięcią.

Jeśli jest to poprawne, myślę, że możesz bezpiecznie używać kodu, pod warunkiem upewnienia się, że wszystko jest w porządku, jeśli chodzi o zarządzanie pamięcią (np. że Twoje metody nie zwracają obiektów, które przydzielają).

 207
Author: sergio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-30 23:28:25

W Twoim projekcie Build Settings , pod inne flagi ostrzegawcze (WARNING_CFLAGS), dodaj
-Wno-arc-performSelector-leaks

Teraz tylko upewnij się, że selektor, który wywołujesz, nie powoduje, że obiekt zostanie zachowany lub skopiowany.

 119
Author: 0xced,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-31 13:57:19

Jako obejście, dopóki kompilator nie zezwoli na nadpisanie ostrzeżenia, możesz użyć runtime

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

Zamiast

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

Będziesz musiał

#import <objc/message.h>
 110
Author: jluckyiv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-16 04:56:52

Aby zignorować błąd tylko w pliku z selektorem wykonaj, dodaj #pragma w następujący sposób:

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

To zignorowałoby ostrzeżenie na tej linii, ale nadal zezwalałoby na to przez resztę twojego projektu.

 88
Author: Barlow Tucker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-18 21:31:49

Dziwne, ale prawdziwe: jeśli akceptowalny (tzn. wynik jest nieważny i nie masz nic przeciwko, aby cykl runloopa był raz), dodaj opóźnienie, nawet jeśli jest to zero:

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

Usuwa to Ostrzeżenie, prawdopodobnie dlatego, że zapewnia kompilator, że żaden obiekt nie może zostać zwrócony i w jakiś sposób niewłaściwie zarządzany.

 67
Author: matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-11 19:19:30

Oto zaktualizowane makro oparte na odpowiedzi podanej powyżej. Ten powinien pozwolić ci zawinąć Kod nawet z instrukcją zwrotu.

#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
Author: syvex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-07 14:58:50

Ten kod nie zawiera flag kompilatora ani bezpośrednich wywołań runtime:

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

NSInvocation pozwala na ustawienie wielu argumentów, więc w przeciwieństwie do {[2] } będzie to działać na dowolnej metodzie.

 31
Author: Benedict Cohen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-01 15:46:50

Cóż, wiele odpowiedzi tutaj, ale ponieważ to jest trochę inne, łącząc kilka odpowiedzi, pomyślałem, że umieścić go w. Używam kategorii NSObject, która sprawdza, czy selektor zwraca void, a także wyłącza Ostrzeżenie kompilatora.

#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
Author: Chris Prince,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-22 19:25:46

Ze względu na potomność, postanowiłem rzucić mój kapelusz na ring:)

[[6]] ostatnio widzę coraz więcej restrukturyzacji z dala od target/selector paradygmat, na rzecz takich rzeczy jak protokoły, bloki itp. Jest jednak jeden zamiennik performSelector, którego używałem już kilka razy:
[NSApp sendAction: NSSelectorFromString(@"someMethod") to: _controller from: nil];

Wydają się być czystym, bezpiecznym dla łuku i prawie identycznym zamiennikiem performSelector bez konieczności dużo o objc_msgSend().

Choć nie mam pojęcia, czy istnieje analogowe dostępne na iOS.

 16
Author: Patrick Perini,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-26 04:29:04

ODPOWIEDŹ Matta Gallowaya na Ten wątek wyjaśnia dlaczego:

Rozważ następujące:

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

Skąd ARC może wiedzieć, że pierwszy zwraca obiekt z liczbą zatrzymań równą 1, a drugi zwraca obiekt, który jest autorelowany?

Wydaje się, że ogólnie bezpiecznie jest tłumić ostrzeżenie, jeśli ignorujesz zwracaną wartość. Nie jestem pewien, jaka jest najlepsza praktyka, jeśli naprawdę chcesz uzyskać zachowany obiekt z performSelector -- poza "nie rób tego".

 15
Author: c roald,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:39

@C-road podaje właściwy link z opisem problemu tutaj . Poniżej możesz zobaczyć mój przykład, kiedy performSelector powoduje wyciek pamięci.

@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;
}

Jedyną metodą, która powoduje wyciek pamięci w moim przykładzie jest CopyDummyWithLeak. Powodem jest to, że ARC nie wie, że copySelector zwraca zachowany obiekt.

Jeśli uruchomisz narzędzie Memory Leak, możesz zobaczyć poniższy obrazek: Tutaj wpisz opis obrazka ...i nie ma wycieków pamięci w żadnym innym przypadku: Tutaj wpisz opis obrazka

 14
Author: Pavel Osipov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:29

Aby makro Scotta Thompsona było bardziej ogólne:

// 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")

Następnie użyj go w ten sposób:

MYSilenceWarning(-Warc-performSelector-leaks,
[_target performSelector:_action withObject:self];
                )
 6
Author: Ben Flynn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-04 18:22:41

Ponieważ używasz ARC musisz używać iOS 4.0 lub nowszego. Oznacza to, że można użyć bloków. Gdyby zamiast zapamiętywać selektor do wykonania zamiast wziąć blok, ARC byłby w stanie lepiej śledzić, co się dzieje i nie musiałbyś ryzykować przypadkowego wprowadzenia wycieku pamięci.

 4
Author: honus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-23 02:33:40

Nie tłumić ostrzeżeń!

Istnieje nie mniej niż 12 alternatywne rozwiązania majsterkowania z kompilatorem.
Podczas gdy jesteś sprytny w czasie pierwszej implementacji, niewielu inżynierów na Ziemi może podążać Twoimi śladami, a ten kod w końcu się zepsuje.

Bezpieczne Trasy:

Wszystkie te rozwiązania będą działać, w pewnym stopniu odbiegając od pierwotnego zamiaru. Załóżmy, że param może być nil Jeśli tak pragnienie:

Bezpieczna droga, takie samo zachowanie konceptualne:

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

Bezpieczna trasa, nieco inne zachowanie:

(Zobacz to odpowiedź)
Użyj dowolnego wątku zamiast [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]];

Niebezpieczne Trasy

wymaga pewnego rodzaju wyciszenia kompilatora, które z pewnością się zepsuje. Zauważ, że w chwili obecnej, to nie włamał się Swift.

// AT YOUR OWN RISK
[_controller performSelector:selector];
[_controller performSelector:selector withObject:anArgument];
[_controller performSelector:selector withObject:anArgument withObject:nil];
 3
Author: SwiftArchitect,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:26

Zamiast używać podejścia blokowego, które sprawiało mi pewne problemy:

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

Użyję NSInvocation, TAK:

    -(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
Author: supersabbath,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-21 08:52:00

Jeśli nie musisz podawać żadnych argumentów, łatwym obejściem jest użycie valueForKeyPath. Jest to możliwe nawet na Class obiekcie.

NSString *colorName = @"brightPinkColor";
id uicolor = [UIColor class];
if ([uicolor respondsToSelector:NSSelectorFromString(colorName)]){
    UIColor *brightPink = [uicolor valueForKeyPath:colorName];
    ...
}
 1
Author: arsenius,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-15 04:08:51

Możesz również użyć protokołu tutaj. Więc utwórz protokół w ten sposób:

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

W klasie, która musi zadzwonić do selektora, masz wtedy właściwość@.

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

Gdy musisz wywołać @selector(doSomethingWithObject:) w instancji MyObject, zrób to:

[self.source doSomethingWithObject:object];
 -2
Author: Damon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-03 11:34:34