15

Piszę API, które obejmuje obsługę zdarzeń, i chciałbym móc używać bloków do obsługi. Połączenia zwrotne często chcą mieć dostęp lub modyfikować siebie. W trybie ARC, Klang ostrzega, że ​​bloki odnoszące się do siebie prawdopodobnie stworzą cykl zachowywania, który wydaje się pomocnym ostrzeżeniem, które ogólnie chcę zachować.Kompaktowe wyłączenie ostrzeżenia o cyklach zatrzymywania łuku dla samoreferencyjnych bloków

Jednak w przypadku tej części interfejsu API cykl życia wywołania zwrotnego i obiektu zawierającego są utrzymywane na zewnątrz. Wiem, że mogę przerwać cykl, gdy obiekt powinien zostać zwolniony.

Mogę wyłączyć ostrzeżenie o cyklu zatrzymywania na podstawie pliku z #pragma clang diagnostic ignored "-Warc-retain-cycles", ale to wyłącza ostrzeżenie dla całego pliku. Mogę otoczyć bloki z ostrzeżeniem #pragma clang diagnostic push i pop, ale to sprawia, że ​​bloki są brzydkie.

Mogę też otrzymać ostrzeżenie, aby odejść, odwołując się do zmiennej __weak wskazującej na siebie, zamiast bezpośrednio odnosić się do siebie, ale to sprawia, że ​​bloki są o wiele mniej przyjemne w użyciu.

Najlepszym rozwiązaniem mam wymyślić jest to makro, które zrobi wyłączenie diagnostyczne wokół bloku:

#define OBSERVE(OBJ, OBSERVEE, PATH, CODE) \ 
[(OBJ) observeObject:(OBSERVEE) forKeyPath:(PATH) withBlock:^(id obj, NSDictionary *change) { \ 
_Pragma("clang diagnostic push") \ 
_Pragma("clang diagnostic ignored \"-Warc-retain-cycles\"") \ 
do { CODE; } while(0); \ 
_Pragma("clang diagnostic pop") \ 
}]; 

który działa, ale nie jest to bardzo wykrywalne dla użytkowników API, nie pozwala zagnieżdżone obserwatorów, i źle współpracuje z edytorem XCode. Czy istnieje lepszy sposób na wyłączenie lub uniknięcie ostrzeżenia?

+10

Tworzenie odwołania '__weak' do' self' dosłownie zajmuje jedną linię kodu. Myślę, że naprawienie problemu w tym przypadku jest lepsze niż próba złagodzenia objawów. W jaki sposób odwołanie się do 'weakSelf' zamiast' self' czyni blok mniej przyjemnym w użyciu? –

+3

Jest mniej przyjemny na kilka sposobów. Słuchacze często są dość krótcy, czasami pojedynczy komunikat. Deklaracja __weak podwaja rozmiar słuchacza. Oznacza to również, że musisz kwalifikować dostęp do nieruchomości, zamiast używać wywnioskowanego siebie. Zgodzę się, że moje obecne rozwiązanie jest prawdopodobnie gorsze niż użycie __weak, ale miałem nadzieję, że dzięki temu pytaniu otrzymam lepszą. –

+1

Czy możesz zmienić prototyp swojego bloku ukończenia, aby zaakceptować argument "własny"? Teraz kod, w którym przekazujesz swoje bloki będzie wyglądał tak samo (z wyjątkiem przyjmowania jednego dodatkowego argumentu) i możesz wyeliminować ostrzeżenia. (tzn. czy twój interfejs API przekazuje przedmiot do twojego bloku) – nielsbot

Odpowiedz

7

Po pierwsze, nie jest to prosty sposób, aby wyłączyć ostrzeżenia dla niektórych linii kodu za pomocą #pragma:

#pragma clang diagnostic push 
#pragma clang diagnostic ignored "<#A warning to ignore#>" 
<#Code that issues a warning#> 
#pragma clang diagnostic pop 

Ale nie będę go używać w tym konkretnym przypadku, ponieważ nie będzie rozwiązać problem , po prostu ukryje to przed programistą. Wolę iść z rozwiązaniem zaproponowanym przez Marka. Aby utworzyć słabego odniesienia, można wykonać jedną z następujących czynności zewnątrz bloku:

__weak typeof(self) weakSelf = self; // iOS ≥ 5 
__unsafe_unretained typeof(self) unsafeUnretainedSelf = self; // 5 > iOS ≥ 4 
__block typeof(self) blockSelf = self; // ARC disabled 
0

nowy LLVM jest lepsze wykrywanie/zapobieganie takie zachowują cykle czekać na LLVM wysyłce z iOS6 czy sposób Alexa z tworzeniem słaby var.

wyłączenie ostrzeżenia to zły pomysł!

1

Myślę, że wyłączenie ostrzeżenia jest obecnie jedynym właściwym sposobem postępowania, ponieważ mówi on o kompilatorze: Nie przejmuj się tym cyklem zatrzymania, jestem tego świadomy i sam będę dysponować obserwatorem. Wprowadzenie słabego odniesienia jest kosztownym rozwiązaniem, ponieważ pochodzi z runtime CPU i narzutem pamięci.

0

napisałem następujące makro, które myślę, jest bardzo mądry ...

#define CLANG_IGNORE_HELPER0(x) #x 
#define CLANG_IGNORE_HELPER1(x) CLANG_IGNORE_HELPER0(clang diagnostic ignored x) 
#define CLANG_IGNORE_HELPER2(y) CLANG_IGNORE_HELPER1(#y) 

#define CLANG_POP _Pragma("clang diagnostic pop") 
#define CLANG_IGNORE(x)\ 
    _Pragma("clang diagnostic push");\ 
    _Pragma(CLANG_IGNORE_HELPER2(x)) 

To pozwala robić różne rzeczy Fun (bez Xcode haranguing Ci), takie jak ..

CLANG_IGNORE(-Warc-retain-cycles) 
[object performBlock:^(id obj){ [obj referToSelfWithoutWarning:self]; }]; 
CLANG_POP 

Możesz umieścić dowolną flagę ostrzegawczą, a Clang zważa na twoje kaprysy ...

CLANG_IGNORE(-Warc-performSelector-leaks); 
return [self performSelector:someIllBegotSelector withObject:arcFauxPas]; 
CLANG_POP 

Potem znowu, ostrzeżenia zwykle są tam bez powodu. Pukawki.

0

Aby rozwiązać problem kłucia tworzenia słabego odniesienia, umieściłem to w makrze. Wykorzystuje preprocesora, aby utworzyć nowy var o tej samej nazwie, ale z przedrostkiem („W”, w tym przypadku; unikałem „słaby”, ponieważ to byłoby przesadą i bałagan więcej zasad kapitalizacji):

#define WEAK_VAR(NAME) __unsafe_unretained typeof(NAME) w##NAME = NAME 

... 
WEAK_VAR(self); 
self.block = ^{ 
    [wself doStuff]; 
}; 

Jeśli, oh, słabe odniesienie nie jest pożądane, nie używaj go! Lubię nielsbot rozwiązanie przekazywania obiektu jako parametru (jeśli to możliwe, oczywiście).

Powiązane problemy