Newsgroup: it.comp.macintosh
Titolo: [Cocoa] come diamine faccio a...

qui c'e' un pezzino di codice che copia n files in uno solo l'ho fatto 
usando il Low-level File Management di Cocoa circa +o- a scopo ddattico
ora per funzionare funziona anche ma c'e' un problemuzzo:
i vari oggetti NSData che vado leggendo si accumulano mano a mano nella 
virtual memory mandando il sistema a impiccarsi quando inizia con gli 
swapping semi-selvaggi
possibile che non ci sia (o che io sia cosi' idiota da non trovarlo) un 
modo di ricilare il buffer tra una lettura/scrittura e la successiva?
abbastanza prevedibilmente se mando un messaggio release dove c'e' il 
commento dopo che ha funzionato tutto senza saturare la memoria virtuale 
si impalla col messaggio
'Executable “JoinMe” has exited due to signal 5 (SIGTRAP).'
uffa!
NSFileHandle *readMe;
NSData *theData;
NSFileHandle *writeMe = [NSFileHandle 	
	fileHandleForWritingAtPath:theFileName];
unsigned i,j,remainder,numiter;	
unsigned long long length,written;
written=0;
for(i=0;i<theFileCount;i++) {
   length = [[theFileLengths objectAtIndex:i] unsignedLongLongValue];
   numiter = (unsigned int)(length / BLOCKSIZE);
   remainder = (unsigned int)(length % BLOCKSIZE);
   readMe = [NSFileHandle fileHandleForReadingAtPath:[theFileNames
	objectAtIndex:i]];
   for (j=0;j<numiter;j++) {
     theData = [readMe readDataOfLength:BLOCKSIZE];
     written+=BLOCKSIZE;
     [writeMe writeData:theData];
/*  [theData release]; */
     NSLog(@"Written %qu of %qu bytes",written,theTotalLength);			
   }
   theData = [readMe readDataOfLength:remainder];
   [readMe closeFile];
   written+=(unsigned long long)remainder;
   [writeMe writeData:theData];
/*[theData release]; */
   NSLog(@"Written %qu of %qu bytes",written,theTotalLength);

 virtuale si impalla col messaggio
Non puoi perche' quell'oggetto e' gia' nell'autorelease pool.
Dovresti rimuoverlo, ma non mi pare che quella api siano pubbliche.
Ciao
Mirko

 del file.
ma anche se non li ho allocati io?
il NSData mi viene fornito dal messaggio readDataOfLength dato 
all'NSHandle: non l'ho allocato io
(questa storia degli aautorelease pul merica comunque qualche 
approfondimento)
(PS wow che bello paralare di qualcosa che non sia il prossimo modello! :-O)

 all'NSHandle: non l'ho allocato io
io con cocoa, nonostante le mie buone intenzione, si puo' dire che non 
abbia ancora cominciato :D quindi prendi il mio intervento con le pinze, 
ma in ogni caso anche io ho capito che la regola da seguire e' che tu 
devi preoccuparti di dealllocare solo gli oggetti che tu hai creato o 
copiato. se ti viene passato un oggetto allocato da qualcun altro e' 
questo qualcun altro che deve occuparsi di deallocarlo.
quando pero' vengono deallocati? questa cosa non l'ho capita bene ma mi 
pare d'aver capito che un oggetto dentro un'autorelease pool vive solo 
per la durata del metodo corrente. quindi nel tuo caso io costruirei un 
ciclo for che chiama un metodo che legge BLOCKSIZE da un file e lo 
scrive nell'altro. in questo modo finito il metodo l'oggetto theData 
dovrebbe essere deallocato.
e' una mera supposizione pero' se ti va di provare sarei curioso di 
sapere se ci ho azzeccato :)

 sapere se ci ho azzeccato :)
pupo permettendo provo (ora la moglie va dal dentista)

 del file.
perfetto! grazie mille, funzica alla grande :-)

 pupo permettendo provo (ora la moglie va dal dentista)
miiii gli dovrei passare 2000 parametri... mi sta passando la voglia 
(anche perche' l'altra soluzione funziona alla grande)

 (questa storia degli aautorelease pul merica comunque qualche approfondimento)
C'e' una sezione che spiega il funzionamento degli autorelease pools 
nella documentazione, (Memory management raggiungibile da NSObject e 
Objects Ownership).
Ciao
Mirko

 pupo permettendo provo (ora la moglie va dal dentista)
allora provato:
no l'autorelease non vive quanto il metodo corrente (cosa che non ha 
senso se pensi che dentro quel metodo tu chiami altri metodi dei vari kit)
rimango dell'idea che l'autorelease pool viene inizializzato ogni volta 
che viene processato un evento, a prescindere da quanto metodi tale 
processamento richieda, e rilasciato prima di prelevare l'evento 
successivo dalla coda

 all'NSHandle: non l'ho allocato io
METODO DELLO SCOLAPASTA:
 - Se un metodo (tranne alloc..., init..., copy...) ti da' un oggetto,
tu lo puoi "lasciar cadere" (i.e. uscire da un metodo senza rilasciarlo)
senza avere leak. -
Corollario: Se tu crei un metodo che ritorna un qualche oggetto,
assicurati di ritenerlo da qualche parte o di autorilasciarlo prima di
ritornarlo.
Puoi usare il trucco di una pool di autorilascio nidificata per
rimuovere esplicitamente questi oggetti "caduti" dalla memoria.
Se tu crei un oggetto (o ricevi un oggetto da metodi chiamati alloc...,
new..., init... o copy...), tu sei responsabile della sua distruzione.
Facile, no?
 - ∞

 METODO DELLO SCOLAPASTA:
sisi tutto chiaro... ora! (mi mancava il trucco del pul interno)
mi confermi che la vita del pul di autorilascio fornito precotto 
dall'Application kit e' il processamento dell'evento corrente?

 dall'Application kit e' il processamento dell'evento corrente?
Si', all'inizio del processing di ogni evento viene creata una nuova
pool di autorilascio e viene rilasciata alla fine (prima che il run loop
faccia un nuovo "giro").
 - ∞

 successivo dalla coda
sto cominciando a perdermi... con evento che intendi? roba del tipo 
"l'utente preme un pulsante"? scusa la banalita' della domanda ma 
preferisco avere tutto chiaro, anche le cose piu' ovvie ;)
per quanto riguarda questa storia dell'autorelease nidificato... mi fai 
vedere il tuo codice dove hai implementato questa roba? cosi' lo 
confronto con l'altro e vedo di capire come diavolo funziona :)

 rimuovere esplicitamente questi oggetti "caduti" dalla memoria.
cosa intendi con "caduti"?

 > rimuovere esplicitamente questi oggetti "caduti" dalla memoria.
"Caduto" nel senso "leakato" ovvero "avevo un riferimento a
quell'oggetto ma poi l'ho lasciato perdere (sovrascritto ecc.) senza
farci nulla (i.e. rilasciato)". Sempre nella metafora dello scolapasta
:)
 - ∞

 confronto con l'altro e vedo di capire come diavolo funziona :)
Be', non e' difficile -- basta aggiungere un NSAutoreleasePool* pool =
[[NSAutoreleasePool alloc] init]; come prima linea del ciclo e un [pool
release]; come ultima.
 - ∞

 cosa intendi con "caduti"?
immagino intenda oggetti che dovrenno essere releseati alla fine del 
processing nell'evento corrente, ma che fintento che questo non finisce 
stanno li' ad occupare memoria inutilmente (gli NSData vecchi a mano a 
mano che i theData viene assegnato a un nuovo valore)

  - ∞
visto che mi sembri ferrato in materia ti chiedo un consiglio: dopo il 
clamoroso successo di ieri :-O! mi sono montato la testa, fatto prendere 
da manie di grandezza e ora mi sono intrippato con bindings 
arraycontrollers pannelli ciascuno nel suo nib file e col suo controller 
e chi piu' ne ha piu' ne metta... veniamo al dunque:
1) per il malditesta cosa mi consigli di prendere?
2) in quanti giorni passa?
3) e' grave dottore?

 confronto con l'altro e vedo di capire come diavolo funziona :)
l'ho giusto messo in altro post (stesso tred)

 3) e' grave dottore?
Si'. Cocoa intrippa parecchio quando uno inizia a usarlo un po' :D
Ma non ti preoccupare, se segui i miei consigli non durera' a lungo...
:D
 - ∞

 in pratica theData viene releseato ad ogni iterazione del ciclo
scusa ma quando rilasci temp come diavolo fa il sistema a rilasciare 
theData? cioe' non vedo nessuna riga di codice in cui "comunichi" a temp 
l'indirizzo di theData, quindi non capisco come possa essere deallocato 
theData quando deallochi temp...

 :)
mh... leakati non nel solito senso pero', perche' questi oggetti stanno 
dentro un'autorelase pool, quindi anche se io "perdo" il loro indirizzo 
questi verranno poi comunque rilasciati (sempre per la regola che non 
avendoli allocati io non sono io che mi devo occupare della loro 
deallocazione). giusto?


avevo tato una risposta assurda: ho cancellato il post e metto quella 
giusta (forse)
il NSData puntato da the data non e' stato retained e quindi, essendo 
assicitao al pool temp quendo questo viene released vengono released 
tutti gli oggetti allocati li dentro che non sono stati esplicitamente 
retained

 deallocazione). giusto?
giusto e' un leak temporaneo: per questo, se serve pulitre prima si usa 
l'autorelease
dopo vengono comunque puliti ma se dopo e' "dopo che hai messo in 
swapping titto il sistema" non e' grache' utille il fatto che non si sia 
una vera leak assoluta

 release]; come ultima.
il codice l'ho visto. non riesco a capire come l'oggetto venga infilato 
nell'autorelease pool...
inoltre gia' che ci sono... io vedo sempre esempi di codice come questo 
che hai fatto tu, e cioe' 
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
ma se per un qualche motivo l'allocazione fallisce? o l'init fallisce? 
non ho ancora visto uno stralcio di codice che si preoccupa di 
controllare se e' andato tutto a buon fine. non dovrebbe essere una 
regola basilare di buona programmazione controllare queste cose? cioe' 
io come faccio a essere sicuro che poi non vado a chiamare un metodo di 
un oggetto che non e' stato allocato?

 retained
ok, ma continuo a non riuscire a capire come theData venga associato a 
temp. magari sono io che non riesco a vedere l'ovvio, pero' ancora non 
capisco :D scusa se ti stresso...

 un oggetto che non e' stato allocato?
in objective C e' legale mandare messaggi a nil
se fallisce l'allocazione mandi il messaggio init a nil che, ovviamente 
non fa nulla, sta a te poi, se vuoi, verificare che pool non sia nil

 non fa nulla, sta a te poi, se vuoi, verificare che pool non sia nil
questo non lo sapevo e neanche ero riuscito a trovarlo :) grazie ancora 
per l'aiuto

 sua deallocazione
Non esattamente.
E' una questione di convenzione. Quando tu chiami un metodo e quello ti
ritorna un oggetto, e' sicuro che a) l'oggetto e' ritenuto da qualcun
altro oppure b) l'oggetto e' stato aggiunto a una pool di autorilascio.
Questo vuol dire che puoi fare una cosa del tipo:
- (void) unMetodo {
        ...
        NSData* data =  [readMe readDataOfLength:remainder];
} // fine del metodo
SENZA rilasciare data prima di uscire dal metodo e SENZA che capitino
memory leak. Questo perche' data e' o ritenuto o autorilasciato da
qualcun altro (i dettagli li gestisce -readDataOfLength:). In questo
caso, dato che readMe non ha interesse a gestirsi l'oggetto NSData, e'
autorilasciato.
Quando invece crei un oggetto tu (tramite alloc-init), sei responsabile
tu della sua distruzione. Pertanto:
- (void) unMetodo {
        NSData* data = [[NSData alloc] init];
} // fine del metodo
e' MALE perche' data non e' ne' autorilasciato ne' ritenuto -- occupera'
spazio in memoria senza che tu possa farci nulla (es rilasciarlo).
Pertanto o lo usi in qualche modo e poi fai [data release], o lo
autorilasci, ad esempio:
- (NSData*) unMetodo {
        NSData* data = [[[NSData alloc] init] autorelease];
        return data;
} // fine
e' ok, ed e' la stessa cosa che fa -readDataOfLength: li' sopra (a parte
leggere il file, ovviamente :D). In questo caso l'oggetto data
sopravvivra' fino alla fine dell'evento (del tuo codice che viene
eseguito) oppure qualcuno potra' ritenerlo per farlo sopravvivere ancora
piu' a lungo (nel qual caso diviene responsabile del suo rilascio,
senno' si ha lo stesso problema di sopra -- un oggetto che non verra'
buttato via ma che e' "irraggiungibile", pertanto non puo' ricevere il
messaggio -release che lo farebbe svanire).
 - ∞

 altro oppure b) l'oggetto e' stato aggiunto a una pool di autorilascio.
ok, forse non mi sono spiegato bene (o magari non ho ancora capito :)
se io creo un oggetto posso fare subito un autorelease e poi non 
preoccuparmi piu' della sua deallocazione, giusto?
comunque oramai credo di aver afferrato il concetto.

 preoccuparmi piu' della sua deallocazione, giusto?
Si' ma e' meno efficiente, perche' l'oggetto rimarra' "in giro" anche
dopo che tu hai finito di usarlo. Dovrai aspettare il prossimo giro del
run loop perche' la memoria si liberi.
Con l'esempio che ci hai fatto l'oggetto era GIA' autorilasciato, per
cui devi usare una pool all'interno del loop. Ma se ne crei uno con
alloc-init, e' un po' piu' efficiente (= veloce) fare un release alla
fine senza dover passare per una pool di autorilascio.
 - ∞

 run loop perche' la memoria si liberi.
ok grazie della precisazione :)

Matteo Cavalleri <shiva....@tiscali.invalid> wrote:

Provo io...?  Il sistema mantiene uno stack di autorelease pool.  Ogni
nuovo oggetto allocato viene associato al pool "corrente" che e` in cima
allo stack al momento della sua allocazione -- ne viene tolto se gli
mandi messaggio retain, ci viene messo se gli mandi messaggio
autorelease.  Quando un pool di autorelease viene mollato, molla a sua
volta tutti gli oggetti associati ad esso.
Quindi "dove viene associato a tempo" e` "nel codice di sistema della
alloc" (perche` temp sta in cima allo stack).
Alex

 nuovo oggetto allocato viene associato al pool "corrente" che e` in cima
ahhhhhhh... capito tutto grazie :)

 alloc"
Non esattamente. Per convenzione tutti i metodi che ritornano un oggetto
o lo ritengono per se' o mandano -autorelease; all'oggetto stesso, che
aggiunge l'oggetto alla pool di autorelease in cima allo stack.
 - ∞