Cóm guardar dades permanentment a la memòria?
Bé, en el següent pas que voldria fer aprenent obj-c seria com guardar algunes dades sense tenir que utilitzar una base de dades (es que mai n'he fet servir i em sembla que de moment en tinc prou amb obj-c)
El cas es que tinc un NSMutableArray on guardo objectes del tipus Persona, aquesta classe només té tres dades, una es el nom(NSString), un altre punts(int), gràfic(int). El que voldria sapiguer es com utilitzar NSKeyArchive per guardar aquestes dades correctament. He utilitzat la classe NSUSerDefaults i he pogut fer alguna cosa, però no em sembla la manera correcta de fer-ho.
He llegit la documentació però em temo que no l'acabo d'entendre, algú em podria fer un petit exemple o donar una explicació senzilla
?
29 juliol 2008 10:43
Si vols desar dades bàsiques: diccionari, array, string, data, dada, numeros. Pots utilitzar les PropertyList. És ràpid i senzill.
Simplement agafa un element d'aquestos i executa la funció:
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)flag
I per llegir l'objecte, ho pots fer directament des de la classe de l'objecte arrel, o directament des del text carregat del fitxer en format NSString:
+ (id)dictionaryWithContentsOfFile:(NSString *)path
- (id)propertyList
30 juliol 2008 10:05
Només dir-te una cosa, si són moltes dades no és la millor opció. I si tens referències cícliques oblida-te'n. En aquest cas utilitza el NSKeyedArchiver i el protocol NSCoding.
30 juliol 2008 10:13
Ho estic intentant fer amb NSKeyedArchiver i el que he fet es implementar la classe NSCoding i definir els mètodes de codificació i descodificació a la classe Persona d'aquesta manera:
@interface Persona : NSObject <NSCoding>
// implementació
- (void) encodeWithCoder:(NSCoder *)coder
{
[super init];
[coder encodeObject:nombre forKey:@"nombre"];
[coder encodeInt:edad forKey:@"edad"];
[coder encodeInt:color forKey:@"color"];
}
- (id) initWithCoder:(NSCoder *)coder
{
[super init];
[self setNombre:[coder decodeObjectForKey:@"nombre"]];
[self setEdad:[coder decodeIntForKey:@"edad"]];
[self setColor:[coder decodeIntForKey:@"color"]];
return self;
}
Per arxivar-ho:
[NSKeyedArchiver archiveDataWithRootObject:people];
l que passa es que no sé com treure-ho de l'arxiu. Tinc un UIView i un UIViewController on tinc casi tot el codi, incloent l'array de persones. He llegit que el que tinc que fer es:
NSMutableArray *newArray=[NSKeyedUnarchiver unarchiveObjectWithData:data];
Però el que no sé es d'on ve la instància de data.
30 juliol 2008 13:07
En principi ho veig bé. Només et recordaria que tinguis en compte que els objectes que codifiques han d'implementar el protocol NSCoding. I també que no és obligatori desar-ho tot.
Bé, acabo de veure un error, al mètode "encodeWithCoder:" ja que hi has ficat un "[super init]". En que potser hi has de ficar és un: 
[super encodeWithCoder:coder];
Respecte a les funcions de l'NSKeyedArchiver pensa que el primer mètode que escrius retorna un "NSData" que l'hauries de desar en un arxiu. Jo de totes formes et recomanaria aquest:
+ (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path
Per recuperar les dades, pots utilitzar el que tu dius, passant-li un NSData que hagis llegit d'un fitxer, o fer-ho directament indicant-li el path del fitxer. Així:
+ (id)unarchiveObjectWithFile:(NSString *)path
Pensa que et retornarà el mateix tipus d'objecte root que vas utilitzar al desar-lo, i tots els objectes que hi estiguin relacionats en tota la jerarquia de dades, perfectament relacionada.
30 juliol 2008 16:33
El codi aquest que "potser" t'has deixat:
[super encodeWithCoder:coder];
[super initWithCoder:coder];
Només cal ficar-lo si la classe pare també implementa el protocol NSCoder. Que ho hauria de fer si hi té dades. Sinó no cal ficar-les, i ja està bé tal com ho has fet tu amb un "init" al mètode d'init:
[super init];
30 juliol 2008 16:37
M'he tornat a deixar una cosa. No t'oblidis de fer això dins de l'initWithCoder. Encara que dubto et falli mai.
if ( self = [super init] ) {
[self setNombre:[coder decodeObjectForKey:@"nombre"]];
[self setEdad:[coder decodeIntForKey:@"edad"]];
[self setColor:[coder decodeIntForKey:@"color"]];
}
return self;Pensa que els init retornen un objecte, i ningú t'assegura que sigui el propi objecte creat amb "alloc", en podria ser un de diferent.
30 juliol 2008 16:40
Això últim que t'he dit em sembla que ho pots comprovar amb la classe NSString. Més o menys així:
NSLog(@"Adreça de la cadena: 0x%X", @"cadena");
id cadena = [NSString alloc];
NSLog(@"Adreça de la cadena: 0x%X", cadena);
cadena = [cadena initWithString:@"cadena"];
NSLog(@"Adreça de la cadena: 0x%X", cadena);
Si surt com espero, l'última assignació t'haurà canviat l'adreça del punter i serà el mateix que la de la cadena estàtica de l'inici.
2008-07-30 16:50:43.484 Prova[3385:10b] Adreça de la cadena: 0x204C
2008-07-30 16:50:43.580 Prova[3385:10b] Adreça de la cadena: 0x10A6D0
2008-07-30 16:50:43.584 Prova[3385:10b] Adreça de la cadena: 0x204C
30 juliol 2008 16:51
La classe persona hereta directament de NSObject (es un exemple senzill), així que no puc fer-hi [super initWithCoder:coder], el que si he fet es posar l'init en un if com m'has recomanat.
Després ja vaig intentar arxivar en directament en un arxiu, però no em va funcionar
. El que no m'havia fixat es que tornava un boolean, així que ho en intentat de la següent manera:
BOOL grabado=[NSKeyedArchiver archiveRootObject:personas toFile:@"dadesDelJoc"];
if(grabado){NSLog(@"arxivat");}
else{NSLog(@"Nos s'ha pogut arxivar");};
En el log mostra que tot ha sortit bé, però quan inicio el programa un altre cop, sempre em dona un valor 0. Ho faig així:
NSMutableArray *newArray;
newArray=[NSUnarchiver unarchiveObjectWithFile:@"dadesDelJoc"];
He llegit que potser no es pot arxivar en l'emulador, però no estic segur de haver-ho entès bé. En el cas del NSUserDefaults si que es guarden les dades.
Per cert, he posat bé la direcció de l'arxiu? No he posat cap barra ni cap extensió.
30 juliol 2008 21:54
No sé si desa l'emulador, i tampoc se on es desa si no fiques la ruta complerta. De totes maneres et recomano aquestes funcions que retornen directoris de l'usuari. Pots mirar d'imprimir-los al log per veure si funcionen i afegir-los-hi el nom del fitxer per provar viam si es desa.
NSTemporaryDirectory();
NSHomeDirectory();
[NSTemporaryDirectory() stringByAppendingPathComponent:@"fitxer.txt"];
30 juliol 2008 22:15
Acabo de fer una prova ràpida, i cada cop que compilo hem genera un nou directori Home.
/Users/Xin/Library/Application Support/iPhone Simulator/User/Applications/2ECF2328-F806-42B4-998E-4AFB7FB92111
/Users/Xin/Library/Application Support/iPhone Simulator/User/Applications/48B394B1-1BAE-466E-9411-7B2053A0B601
El directori temporal és sempre el mateix. Valdria la pena saber si es poden compartir dades entre aplicacions (suposo que no).
30 juliol 2008 22:25
Em sembla que no he entès el que m'has volgut dir en aquests dos últims posts.. Resumit, cada vegada que has fet la prova , t'ha tornat una nova direcció, no? Això explica que no trobi es trobin les dades. Però a les hores, no estic segur de estar fent-ho correctament. Tindré que esperar a provar-ho al iPhone 
Sobre compartir dades entre aplicacions, no estic segur, no he vist res, l'únic que recordo es que s'envien dades d'una aplicació a un altre per URL. Potser si fas una base de dades amb sql, però d'això encara no sé res.
30 juliol 2008 23:46
Doncs que si et cal un fitxer en un directori absolut, pots utilitzar aquelles dues funcions afegint-li el nom del fitxer, com et mostro en la última línia.
Com t'he dit, el directori temporal, sempre és el mateix, així que pots utilitzar-lo per a desar dades que et serveixin en diferents compilacions.
El directori "home" canvia en compilacions diferents, però potser és el mateix a l'engegar l'aplicació directament des de l'emulador sense compilar-la. De totes formes, pots continuar treballant amb el directori temporal, i quan tinguis l'iPhone canviar-lo.
De bases de dades només se'n poden fer amb SQLite.
31 juliol 2008 08:52
Bé, ja m'he adonat de l'error... estava arxivant amb NSKeyedArchiver i ho recoperava amb NSUnarchiver en comptes de NSKeyedUnarchiver...
Però ja quasi funciona! Es recuperen les dades, però quan intento editar de nou una persona, es penja, però em sembla que l'error ja no té res a veure amb l'arxivador 
Al final ha sigut clau el NSTemporaryDirectory(), ja que sense ell no funciona.
Bé, quina alegria, ara ja puc continuar, gracies!
31 juliol 2008 11:39
Doncs l'error no ho acabo d'entendre, però resulta que després de recuperar les dades en l'array "persones", l'hi tinc que fer un retain, sinó la informació es perd. Suposo que el mètode NSKeyedUnarchiver no prepara la memòria, es així?
31 juliol 2008 12:20
Jaime, les dades de les funcions que retornen dades cal retenir-les si vols utilitzar-les una bona estona i que no desapareguin. Mai ningú de te les reté per tu. Has de considerar si només les utilitzaràs un moment, llavors te n'oblides o et calen per molta estona i llavors cal retenir-les, ja que si algú l'allibera et quedaràs amb un punter que no apunta enlloc. 
31 juliol 2008 19:12
Si, es el que dius, el que passa es que hi han tants conceptes nous per a mi que em costa recordar-los tots :d ! Estava pensant en fer-me un tatuatge...
31 juliol 2008 20:56
Tranquil, a mi també em passa. De vegades és desesperant. Però de mica en mica.
31 juliol 2008 21:03
