15

sto scrivendo un'API che coinvolge gestione degli eventi, e vorrei essere in grado di utilizzare i blocchi per i gestori. I callback spesso vogliono accedere o modificare se stessi. In modalità ARC, Clang avverte che i blocchi di referenziamento di sé probabilmente creeranno un ciclo di conservazione, che sembra un utile avvertimento che voglio continuare in generale.modo compatto disattivare arco mantenere cicli di avvertimento da blocchi autoreferenziali

Tuttavia, per questa porzione del mio API, il ciclo di vita della richiamata e l'oggetto contenente vengono mantenuti esternamente. So che posso rompere il ciclo quando l'oggetto dovrebbe essere deallocato.

posso spegnere il conservano avvertimento ciclo su una base per file con #pragma clang diagnostic ignored "-Warc-retain-cycles", ma che disabilita l'avviso per l'intero file. Posso circondare i blocchi con un #pragma clang diagnostic push e pop intorno a quell'avviso, ma questo rende i blocchi brutti.

Posso anche fare in modo che l'avviso scompaia facendo riferimento a una variabile debole che punta al sé invece di fare riferimento direttamente a se stesso, ma che rende i blocchi molto meno piacevoli da usare.

La soluzione migliore che è venuta in mente è questa macro che fa la disabilitazione di diagnosi intorno al blocco:

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

che funziona, ma non è molto rilevabile per gli utenti API, non permette nidificato osservatori, e interagisce male con l'editor di XCode. C'è un modo migliore per disabilitare o evitare l'avviso?

+10

Creare un riferimento '__weak' a' self' prende letteralmente una riga di codice. Penso che risolvere il problema in questo caso sia meglio che cercare di alleviare i sintomi. In che modo referenziare 'weakSelf' invece di' self' rendendo il blocco meno piacevole da usare? –

+3

È meno piacevole in un paio di modi. Gli ascoltatori sono spesso piuttosto brevi, a volte una singola affermazione. La dichiarazione __weak raddoppia la dimensione dell'ascoltatore. Significa anche che è necessario qualificare gli accessi alle proprietà piuttosto che usare un sé dedotto. Concordo sul fatto che la mia soluzione attuale è probabilmente peggiore dell'utilizzo di __weak, ma speravo di ottenerne una migliore tramite questa domanda. –

+1

È possibile modificare il prototipo del blocco di completamento per accettare un argomento "self"? Ora il codice in cui passi i blocchi apparirà lo stesso (ad eccezione dell'accettazione di un argomento in più) ed è possibile eliminare gli avvertimenti. (Ad esempio, se la tua API passa l'oggetto in questione al tuo blocco) – nielsbot

risposta

7

Per cominciare, c'è un modo semplice per disattivare gli avvisi per alcune linee di codice utilizzando #pragma:

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

Ma io non lo uso in questo caso particolare, perché non risolve il problema , lo nasconderà allo sviluppatore. Preferirei andare con una soluzione proposta da Mark. Per creare un riferimento debole, si può fare in uno dei seguenti al di fuori del blocco:

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

il nuovo LLVM è meglio a individuare/prevenire tale conservano cicli attesa per la spedizione LLVM con iOS6 o do modo di Alex con la creazione di una var debole

disabilitare l'avviso è una cattiva idea!

1

Penso che disabilitare l'avviso sia attualmente l'unico modo corretto di fare, dal momento che dice il compilatore: Non preoccuparti di questo ciclo di conservazione, ne sono consapevole e disporrò io stesso dell'osservatore. L'introduzione di un riferimento debole è una soluzione costosa poiché include CPU di runtime e sovraccarico della memoria.

0

ho scritto la seguente macro, che credo, è molto intelligente ...

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

Esso consente di fare ogni sorta di cose divertenti (senza Xcode si arringare), come ad esempio ..

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

Si può mettere in qualsiasi bandiera di avvertimento e Clang sarà ascoltare i vostri capricci ...

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

Poi di nuovo, gli avvertimenti solito ci sono per un motivo. Popper poppers.

0

Per risolvere il problema del clunkiness della creazione di un riferimento debole, l'ho inserito in una macro. Esso utilizza il preprocessore per creare un nuovo var con lo stesso nome ma con un prefisso ('w', in questo caso, ho evitato 'debole' perché sarebbe eccessivo e la mensa di più con le regole di capitalizzazione):

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

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

Se, otoh, un riferimento debole non è desiderabile, non usarlo! Mi piace la soluzione nielsbot di passare l'oggetto come parametro (quando possibile, ovviamente).

Problemi correlati