Conozco los principios básicos sobre administración de memoria (retención de conteo, autorelease piscinas, etc.) en Cocoa, pero una vez que pasa más allá de retención/liberación simple, se vuelve un poco más confuso No pude encontrar respuestas decentes para ellos, ya que la mayoría de los tutoriales cubren escenarios simples. Me gustaría preguntar acerca de las mejores prácticas sobre cómo escribir el código y evitar fugas.Administración de memoria Objective-C, analizador xml y otros ejemplos no triviales
primera pregunta - iteraciones y asignaciones temporales:
for (id object in objectArray) {
Model *currentItem = object;
/* do something with currentItem */
[currentItem release];
}
Si quito la liberación en la última línea del código funciona bien, pero no hay fugas. ¿Cuál es la regla aquí? El objeto ya está allí en objectArray. Lo estoy asignando directamente, para obtener el tipo. ¿Debería hacer esto de otra manera? ¿Esta asignación aumenta retenciónCuenta de currentItem? (¿Es algo así como [[alloc] initWithObject]?) ¿Cómo saber si esta asignación (objeto) se libera automáticamente o no?
pregunta segundo - instantánea conserva:
Model *model = [unarchiver decodeObjectForKey:@"ARCHIVED_MODEL_OBJECT"];
// it has to be here, because (I was told) unarchiver will return autorelease object
[model retain];
label.text = model.data;
cómo alguien sabía que este método particular trabaja tan extraño, que tengo que llamar inmediatamente a retener el valor devuelto, o voy a topar en nulo en el próximo ¿asignación? No pude encontrar nada como esto en la documentación. De acuerdo con las reglas de retención/liberación, esperaría que decodeObjectForKey devuelva el objeto autorelasado, pero tomará un tiempo hasta que el control regrese a la aplicación y el grupo reclame el objeto modelo que se lanzará. ¿Hay alguna regla para eso? ¿Cómo debo buscar esos?
3ª Pregunta - autoreleasing y que pasan variables:
- (IBAction) loadXMLButtonClicked:(id) sender {
objectArray = [self loadData]; // 1 - objectArray is instance var
NSArray *objectArray = [self loadData]; // 2 - objectArray is local var
// loadXMLButtonClicked is called on button click, here the method finishes
// and control goes back to application, autorelease pool is cleaned?
// case 1 - objectArray stays retained in instance variable? (because setter was used)
// case 2 - objectArray is soon to be released, there were no retains?
// (ignore the fact that it's local var, just hypothetically)
}
- (NSArray *) loadData {
NSArray *objectArray = [[NSArray alloc] init];
// populate array here
return [objectArray autorelease];
}
cuarta pregunta - (tengan paciencia conmigo, último) analizador XML mejores prácticas: (no quiero usar otras soluciones, usando el analizador estándar es practicar el flujo y la administración de la memoria objetivo-c)
Básicamente este código aquí funciona, funciona bien y no tiene fugas, pero realmente no sé si esto es el enfoque adecuado. Tengo un objeto separado que actúa como analizador, analiza un XML para recopilar una matriz de objetos del tipo Modelo. Ahora, después de realizar el análisis, externamente me gustaría obtener esa matriz, aunque no sé cómo (está copiando la matriz y lanzando una buena idea para el analizador completo). Por favor revise el código y vea los comentarios.
He depurado este código muchas veces, usando gdb
para imprimir retenerCounts, zombies, etc. y aunque puedo conseguirlo sin fugas, no sé el 100% por qué y me gustaría escuchar un buen razonamiento, ¿cómo debe hacerse esto con la explicación? Eso sería muy apreciado.
Controller.m
- (NSArray *) loadData {
(...)
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
ModelXMLParser *parserDelegate = [[ModelXMLParser alloc] init];
[parser setDelegate:parserDelegate];
[parser parse];
objectArray = [[parserDelegate objectArray] copy];
// is this ok? *i* don't need the parser object so I think I should get rid of it
// and copy the data. How this copy works, is it shallow (only new reference to array)
// or deep copy (objects allocated again as well)?
// how to do deep copy of NSArray?
[parserDelegate release];
[parser release];
}
ModelXMLParser.m (simplificado)
@implementation ModelXMLParser
@synthesize objectArray; // array of objects
@synthesize currentObject; // temporary object
@synthesize currentChars; // temporary chars
@synthesize parseChars; // parse chars only when there's need, leave those /t/n etc
- parser didStartElement (...) {
if ([elementName isEqualToString:@"objects"]) {
objectArray = [[NSMutableArray alloc] init];
}
else if ([elementName isEqualToString:@"object"]) {
currentObject = [[Model alloc] init];
}
else if ([elementName isEqualToString:@"name"]) {
// do I have to init currentObject.name (NSString) here? I guess not
[self setParseChars:YES]; // just set the flag to make parse control easier
}
else if ([elementName isEqualToString:@"number"]) {
// int isn't object anyway, no init
[self setParseChars:YES]; // just set the flag to make parse control easier
}
}
- parser foundCharacters (...) {
if (parseChars) {
currentChars = [[NSString alloc] initWithString:string];
// why is currentChars retainCount = 2 here?
// is it like currentChars = [NSString new] and then currentChars = string? (so retain once more)
// is it good way to control parser? (please ignore the NSMutableString and appending example, try this one)
// should I just do currentChars = string here?
[currentChars autorelease]; // this is currently my solution, because there's no leak, but I feel it's incorrect
}
}
- parser didEndElement (...) {
if ([elementName isEqualToString:@"object"]) {
[objectArray addObject:[currentObject copy]]; // should I copy here or just addObject, it retains anyway?
[currentObject release]; // I've initialized currentObject before, now I don't need it, so I guess retainCount goes to 0 here?
}
else if ([elementName isEqualToString:@"name"]) {
currentObject.name = currentChars; // is this correct, or shoud I do [currentChars copy] as well?
[self setParseChars:NO];
[currentChars release]; // as before, initialized, now releasing, but is this really correct?
}
else if ([elementName isEqualToString:@"number"]) {
currentObject.number = [currentChars intValue]; // is this correct, or shoud I do [currentChars copy] as well?
[self setParseChars:NO];
[currentChars release]; // as before, initialized, now releasing, but is this really correct?
}
}
- (void) dealloc {
// I shouldn't release currentChars or currentObject, those (I suppose) should be freed after parsing done,
// as a result of earlier releases?
[objectArray release];
[super dealloc];
}
modelo.m
@implementation Model
@synthesize name; // this is NSString
@synthesize number; // this is int
- (id) copyWithZone:(NSZone *) zone {
Model *copy = [[[self class] allocWithZone:zone] init];
copy.name = [self.name copy];
copy.number = self.number;
return copy;
}
- (void) dealloc {
[name release];
// I don't have to release int, right? it's not an object
[super dealloc];
}
estoy especialmente confundida con la pregunta 4. Lo siento por tal vez demasiado largo pregunta, pero esto en realidad se trata de un tema y más profunda comprensión de la misma.
Hace sentido para mí. Pero si no hago esto, tengo pérdida de memoria. La matriz de objetos es un objeto liberado automáticamente, así que no debería lanzarlo. ¿Por qué la fuga? – rattkin
¿Cómo sabes que tienes una fuga? Epatel tiene razón, no debes liberar el objeto. ¿No tienes la oportunidad de hacer algo como [objectArray addObject: [[foo alloc] init]? Esa sería la verdadera causa de la fuga. – zoul