NSError și __autoreleasing (Programare, Obiectiv C, Numărarea Automată A Ref.)

chinabuffet a intrebat.

Poate cineva să-mi explice, vă rog, care este scopul de a avea __autoreleasing în următorul exemplu de bloc de cod?

- (void)execute:(NSError * __autoreleasing *)error {
    // do stuff, possibly assigning error if something went wrong
}

Am eliminat blocul __autoreleasing și totul pare în continuare să compileze/execute fără probleme. Am început să folosesc obj-c post ARC, așa că nu am învățat/înțeles niciodată cu adevărat toate acele chestii cu sublinieri duble. Am citit Ghidul de tranziție ARC, , dar nu înțeleg pe deplin exemplul lor NSError.

1 răspunsuri
CRD

Luați în considerare modul în care ARC lucrează cu variabile – fiecare variabilă de referință are un mod (implicit sau explicit): puternic, , slab, , etc. Acest mod permite ARC să știe cum să gestioneze citirile și scrierile în acea variabilă; de exemplu, pentru o variabilă puternică citirea variabilei nu necesită nicio acțiune suplimentară, în timp ce scrierea necesită eliberarea referinței existente în variabilă înainte de a fi înlocuită cu cea nouă. Pentru a funcționa, ARC trebuie să cunoască modul oricărei variabile.

Să luăm acum în considerare variabilele care sunt la rândul lor transmise prin referință, , de exemplu, pentru execute veți avea un apel de genul

NSError *myError = nil;
...
[someObject execute:&myError]; // pass the variable itself by reference, not the variables value

și corpul lui execute va conține o atribuire de tipul:

- (void)execute:(NSError * __autoreleasing *)error
{
   ...
   if (error != NULL)
      *error = [NSError ...]; // assign indirectly via the reference to a variable
   ...
}

Acum, pentru această atribuire indirectă, ARC trebuie să cunoască modul variabilei la care se face referire, astfel încât să știe cum să citească și să scrie. Aceasta este ceea ce trebuie să facă __autoreleasing în declarație, îi spune lui ARC că i s-a transmis o referință la o variabilă al cărei mod este autoreleasing, , iar acest lucru îi spune lui ARC cum să citească și să scrie conținutul variabilei. Eliminați __autoreleasing și se va presupune un mod implicit, iar în acest caz aș sugera că este bine să fii explicit.

Adresa autoreleasing înseamnă că variabila conține o referință care nu este deținută, iar citirile ar trebui să se facă în modul să păstreze dacă este necesar, iar scrierile pot doar să scrie. Este utilizat în principal pentru variabilele transmise prin referință.

S-ar putea să observați că în exemplul de mai sus variabila myError are modul strong (implicit) și totuși este transmisă prin referință ca autoreleasing – Compilatorul gestionează acest lucru în mod automat prin introducerea unei variabile autoreleasing temporare, copiind fără păstrarea referinței curente în myError în aceasta, și trecând temporar prin referință ca argument pentru execute:. După ce apelul se întoarce, compilatorul efectuează o atribuire normală de la variabila temporară la myError, care are ca rezultat eliberarea oricărei referințe vechi și păstrarea celei returnate.

Pentru mai multe detalii, a se vedea Notele de lansare ale Apple privind tranziția la ARC

Urmare a comentariilor

Î: Este __autoreleasing setat implicit?

R: Ei bine Apple’s Apple nu este specific, dar documentația Clang spune că este implicit pentru parametrii indirecți. La fel ca mai sus, aș recomanda să fie explicit, claritatea este un lucru bun™.

Î: Contează plasarea?

R: Da, și nu… Aceasta este o declarație în C, o întrebare de test („Ce declară următoarele…”). Calificatorul ar trebui să fie între cele două asteriscuri, deoarece este un pointer către o (variabilă de tip) pointer cu eliberare automată către un obiect, , dar Apple declară că compilatorul este „iertător” fără a preciza ce anume iartă. Mergeți la sigur, puneți-l în locul potrivit.

Î: Nu ar trebui să testați pentru error fiind NULL înainte de a face atribuirea indirectă?

R: Bineînțeles că ar trebui, undeva înainte de a face indirecția. Codul prezentat este doar o schiță și astfel de detalii au fost eludate și acoperite de către ...‘s. Cu toate acestea, deoarece a fost ridicată de câteva ori de-a lungul anilor, poate că am eludat prea mult, ar trebui să se găsească o soluție adecvată. if a fost adăugat.

Comentarii

  • Așadar, este __autoreleasing setat implicit dacă nu îl setez în mod explicit? De asemenea, se aplică plasarea lui __autoreleasing contează (înainte, în interiorul sau după asteriscuri)? –  > Por chinabuffet.
  • @chinabuffet – A se vedea urmarea în răspuns –  > Por CRD.
  • @CRD frumos răspuns. Dar poate doriți să subliniați că ar trebui să verificați dacă error este NULL înainte de a atribui la *error. În caz contrar, metoda dvs. ar putea bloca aplicația, deoarece [foo execute:NULL] este o execuție perfect validă și este o modalitate de a spune „Nu-mi pasă de eroare”. –  > Por Joel.
  • Se pare că în XCode 6.1, dacă nu sunteți explicit, primiți un avertisment: „Method parameter of type ‘NSError *__autoreleasing *’ with no explicit ownership” – –  > Por Mickaël Rémond.
  • De acord cu @Joel, trebuie să verificați dacă error NU este nil OR NULL înainte de a atribui *error sau ați putea arunca un EXC_BAD_ACCESS semnal. –  > Por pxpgraphics.