NSArray acts weirdly with objects going out of scope - objective-c

I have a weird problems with NSArray where some of the members of the objects in my array are going out of scope but not the others:
I have a simple object called Section.
It has 3 members.
#interface Section : NSObject {
NSNumber *section_Id;
NSNumber *routeId;
NSString *startLocationName;
}
#property(nonatomic,retain) NSNumber *section_Id;
#property(nonatomic,retain) NSNumber *routeId;
#property(nonatomic,retain) NSString *startLocationName;
#end
#implementation Section
#synthesize section_Id;
#synthesize routeId;
#synthesize startLocationName;
//Some static finder methods to get list of Sections from the db
+ (NSMutableArray *) findAllSections:{
- (void)dealloc {
[section_Id release];
[routeId release];
[startLocationName release];
[super dealloc];
}
#end
I fill it from a database in a method called findAllSection
self.sections = [Section findAllSections];
In find all sections I create some local variables fill them with data from db.
NSNumber *secId = [NSNumber numberWithInt:id_section];
NSNumber *rteId = [NSNumber numberWithInt:id_route];
NSString *startName = #"";
Then create a new Section and store these local variable's data in the Section
Section *section = [[Section alloc] init];
section.section_Id = secId;
section.routeId = rteId;
section.startLocationName = startName;
Then I add the section to the array
[sectionsArray addObject:section];
Then I clean up, releasing local variables and the section I added to the array
[secId release];
[rteId release];
[startName release];
[locEnd_name release];
[section release];
In a loop repeat for all Sections (release local variables and section is done in every loop)
The method returns and I check the array and all the Sections are there. I cant seem to dig further down to see the values of the Section objects in the array (is this possible)
Later I try and retrieve one of the Sections
I get it from the array
Section * section = [self.sections objectAtIndex:row];
Then check the value
NSLog(#" SECTION SELECTED:%#",section.section_Id);
But the call to section.section_Id crashed as section.section_Id is out of scope.
I check the other members of this Section object and they're ok.
After some trial and error I find that by commenting out the release of the member variable the object is OK.
//[secId release];
[rteId release];
[startName release];
[locEnd_name release];
[section release];
My questions are:
Am I cleaning up okay?
Should I release the object added to an array and the local variable in the function?
Is my dealloc okay in Section?
Does this code look ok and should I be looking elsewhere for the problem?
I'm not doing anything complicated just filling array from DB use it in Table Cell.
I can comment out the release but would prefer to know why this works, and if I shouldn't be doing this. The only place that secId is released is in the dealloc.

You should not be releasing secId, rteId, or startName. secId and rteId are pointers to NSNumber instances created with a factory method that returns an already-autoreleased object. Static strings (i.e. #"") do not need to be released. You need to re-read the Memory Management Programming Guide. Then read it again ;-) It will be your friend.

You're releasing objects you don't own. You should read the memory management rules.

I'll second (third) the suggestion to read the memory management rules.
The TL;DR version is anything you alloc and call a method with init in the method name on is your responsibility to release. For instance:
NSString *string = [[NSString alloc] initWithFormat:#"%#", someObject];
In this case you must release string. However:
NSString *string = [NSString stringWithFormat:#"%#", someObject];
Here string is autoreleased. It's basically equivalent to this:
NSString *string = [[[NSString alloc] initWithFormat#"%#", someObject] autorelease];
...meaning that the next time through the event loop (which means possibly as soon as your function returns), the system will send a release message to it for you. Apple calls these "convenience methods".
If you have something like this:
NSString *string = #"foo";
Then string is pointing to an instance of NSString that is created by the runtime when your program initializes and won't go out of scope until your program terminates. Never release these either.
Again, read the guidelines and bookmark them. But this should answer your direct question.

Related

NSString copy or alloc? Do you feel lucky?

I have a thread that needs information from the GUI before starting. What I mistakenly tried to do at first was create pointers to the NSTextFields like so:
NSString *info = [gui_field stringValue];
//launch thread
[self performSelectorInBackground:#selector(myMethod:) withObject:info];
This caused problems when I tried to manipulate "info" from within the thread. I assume this is the case because technically, it was still pointing to the string representation of the NSTextField outside the thread.
This fixed the problem:
NSString *info = [[gui_field stringValue] copy];
I assume this made a copy (with its own memory space) that did not rely on the NSTextField at all. I also assume this should be thread-safe.
Is this the appropriate way to do this? I suppose I could have done this:
NSString *info = [[NSString alloc] initWithString:[gui_field stringValue]];
Are the two producing the same result? And do I have to explicitly call release on the string when using "copy" or is it autoreleased by default?
Update: or, perhaps I could just send a pointer to the thread, and copy the string with "autorelease," adding it to the thread's autorelease pool:
NSString *info = [gui_field stringValue];
//launch thread
[self performSelectorInBackground:#selector(myMethod:) withObject:info];
-(void)myMethod:(NSString*)info
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *copied_str = [[info copy] autorelease];
//do stuff
[pool drain];
//copied_str should now be history
}
This way, I don't have to worry about explicitly releasing copied_str. It will be gone once the thread ends.
No need to rely on luck :)
alloc, copy, new and mutableCopy mean you own the object. Both of those will give you a retained object. If you're managing memory, you need to release it.
By convention, other methods will give you an autoreleased object.
As an example, if you want an autoreleased object, you can call:
NSString *str = [NSString stringWithString:yourString];
See the memory management guide:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html
Specifically the four rules here:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html
You own any object you create
You create an object using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy” (for example, alloc, newObject, or mutableCopy).
Finally, both will copy the string.
from the NSString docs:
initWithString:
Returns an NSString object initialized by copying the characters from another given string.
copy is from NSObject. It defines copy as:
Return Value
The object returned by the NSCopying protocol method copyWithZone:, where the zone is nil.
NSString implements the NSCopying protocol so copy will return a copy of the string.
There is one exception where the string isn't copied by initWithString - if you pass a string literal it will wrap the pointer to the constant and ignore retain/release. See here if you're curious: Difference between NSString literals
NSString *info = [[gui_field stringValue] copy];
NSString *info = [[NSString alloc] initWithString:[gui_field stringValue]];
Those do pretty much the exact same thing.
[self performSelectorInBackground:#selector(myMethod:) withObject:info];
-(void)myMethod:(NSString*)info
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *copied_str = [[info copy] autorelease];
No, you can't do that. Even accessing the GUI string object just to copy it could be enough for a crash.
I think this is a case where the usually recommended patterns for memory management really don't provide a great solution, so you can go outside them.
NSString *info = [[gui_field stringValue] copy];
//launch thread
//pass ownership to callee
[self performSelectorInBackground:#selector(myMethod:) withObject:info];
// myMethod owns info!
-(void)myMethod:(NSString*)info
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[info autorelease];
Another option is retain infoCopy as an instance variable somewhere. That would let you use normal memory management patterns, but it doesn't fit semantically. It's really not an instance variable, it's an argument.
"I asked for an argument."
"This is abuse."

NSMutableDictionary error

I want to use NSMutableDictionary to cache some data i will use later. My custom object is following:
#interface MyData : NSObject {
NSRange range;
NSMutableArray *values;
}
#property (nonatomic, retain) NSMutableArray *values;
and implement:
- (id)init {
if (self = [super init]) {
values = [[NSMutableArray alloc] init];
}
return self;
}
and when i wanna cache it, i use it like this:
NSMutableDictionary *cache = [[NSMutableDictionary alloc] init];
NSString *key = #"KEY";
MyData *data = [[MyData alloc] init];
// save some data into data
[data.values addObject:"DATA1"];
[data.values addObject:"DATA2"];
//... ...
[cache setObject:data forKey:key];
My questions is the count of cache.values is zero when i retrieve this object later as follow:
[cache objectForKey:#"KEY"];
i can retrieve "data" and the object's memory address is the same as the address when i put it into cache.
what's wrong? i need some kind guys help, any info is helpful. thanks
As Carl Norum pointed out, you're passing C strings to addObject:. addObject:, as its name suggests, requires a pointer to a Cocoa object; a C string is a pointer to characters. You need to pass NSString objects there; for literal strings, this simply requires prefixing them with #: "Fred" is a constant C string, whereas #"Fred" is a constant NSString object.
Is cache an instance variable? It looks like it's not; it appears to be a local variable, which means you're creating a new dictionary object every time. That's why there's nothing you've added previously (to previous dictionaries) in the new one. It also means you're leaking those previous dictionaries, since you're not releasing them (not in the code you showed, anyway).
Make cache an instance variable and only create the dictionary when you don't already have one (i.e., when cache == nil). Creating the dictionary in your init method is one good way. And make sure you manage its lifetime appropriately, so you don't leak and/or crash.
First of all your objects your adding don't look right it should have an # before the string. Like #"DATA1"
Second when you add an object to a dictionary or an array it does not make an actual copy of it. It just creates a pointer to it so if those objects are destroyed or moved somewhere also they are also gone out of your dictionary. A better way to make a cache of your values would be to copy the objects like so:
MyData* cache = [[MyData alloc] init];
for (int i = 0; i < [data.values count]; i ++){{
[cache.values addObject:[NSString stringWithString:[data.values objectAtIndex:i]]];
}
Don't use a dictionary in this situation.

Confused about alloc and release

I'm a little confused about Objective-C and allocating/releasing objects.
If I do this:
NSString *myString;
if([someString isEqualToString: #"test1"]){
myString = #"got 1";
}else{
myString = #"got 2";
}
Do I have to release myString after that?
And the same with self-defined objects:
myOwnObject *someObject = [someArray objectAtIndex: 1];
myButton.label1.text = someObject.name;
Do I have to release someObject?
The reason why I'm asking is that I get memory-leaks in a method and I can't find where it is. So I'm trying to figure out whether I do the alloc/release stuff correctly.
The leak occurs on a NSPlaceholderString (I guess that's somewhere hidden in my NIB-File).
Also - if I have an object, allocate it, but only use some of the properties, but DO a release of every property on dealloc - will this cause memory leaks?
Sorry - hope my questions do make at least some sense :)
Thanks for any help!
Listen to me. THIS IS THE ONLY RULE THAT MATTERS.
If you use a method with "copy", "alloc", "new", or "retain" in the name
You own the object and MUST later release or autorelease it.
If you don't:
Don't!
But don't expect the object to stick around outside of that scope, because you don't own it.
It's that simple.
MyClass *foo = [[MyClass alloc] init];
[array addObject:foo];
[foo release];
Did you use "copy", "retain", "new", or "alloc"? Yes. Release it.
MyClass *someObject = [someArray objectAtIndex:0];
Did you use "copy", "retain", "new", or "alloc"? No. Don't release it.
BUT
If you have an instance variable which you need to access in other methods:
ivar = [[someArray objectAtIndex:0] retain];
Then you're guaranteed it will stick around because you own it.
(Another way to handle this is with #property (retain) properties, because then you can do self.ivar = someObject and it'll retain it for you.)
But remember to release them in -dealloc!
No, you don't have to release either of those. I usually release only objects that I alloc, such as this snippet:
NSString *string = [[NSString alloc] initWithFormat:#"%#", something];
// yadda yadda yadda some code...
[string release];
To answer your first question, you don't need to release strings created with the #"" syntax.
On your second example, you should not have to release someObject. However, a problem could arise if your dealloc method in your myOwnObject class does not correctly release all of its instance variables.

Memory leak for NSDictionary loaded by plist file

I have a memory leak problem that just can not understand! Watch this initialization method:
- (id)initWithNomeCompositore:(NSString *)nomeCompositore nomeOpera:(NSString *)nomeOpera {
if (self = [super init]) {
NSString *pathOpere = [[NSBundle mainBundle] pathForResource:kNomeFilePlistOpere ofType:kTipoFilePlist];
NSDictionary *dicOpera = [NSDictionary dictionaryWithDictionary:
[[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
objectForKey:nomeCompositore]
objectForKey:nomeOpera]];
self.nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
self.compositore = [[NSString alloc] initWithString:nomeCompositore];
self.tipologia = [[NSString alloc] initWithString:[dicOpera objectForKey:kKeyTipologia]];
}
return self;}
Then this little variation (note self.tipologia):
- (id)initWithNomeCompositore:(NSString *)nomeCompositore nomeOpera:(NSString *)nomeOpera {
if (self = [super init]) {
NSString *pathOpere = [[NSBundle mainBundle] pathForResource:kNomeFilePlistOpere ofType:kTipoFilePlist];
NSDictionary *dicOpera = [NSDictionary dictionaryWithDictionary:
[[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
objectForKey:nomeCompositore]
objectForKey:nomeOpera]];
self.nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
self.compositore = [[NSString alloc] initWithString:nomeCompositore];
self.tipologia = [[NSString alloc] initWithString:#"Test"];
}
return self;}
In the first variant is generated a memory leak, the second is not! And I just can not understand why! The memory leak is evidenced by Instruments, highlighted the line:
[NSDictionary dictionaryWithContentsOfFile:pathOpere]
This is the dealloc method:
- (void)dealloc {
[tipologia release];
[compositore release];
[nomeCompleto release];
[super dealloc];}
Remember that alloc returns an object that you own.
If you declared your three string properties as retain, assigning those objects to your properties means you now own each one twice—once because you allocked it, and again because you assigned it to your property. The objects remain alive because nothing releases their second ownerships.
If you declared the properties as copy (which is the correct way to declare an NSString property), assigning the object there stores a copy as the value of the property. You do nothing further with the original objects, which remain alive because nothing releases them.
Either way, that is your leak.
The property should be declared as copy; if it already is, don't try to fix the leak by changing that.
You should not use property access here. Remember that assigning to a property is a set<PropertyName>: message, and that your object is not fully initialized yet. Sending a message to an incompletely-initialized or incompletely-deallocated object is asking for trouble, particularly when subclasses are involved, since they may override the accessor methods in ways the superclass doesn't expect.
So, in init only, assign directly to the instance variables. In dealloc only, send release messages directly to the objects in the instance variables. Everywhere else, use property accesses.
You also should not use alloc and initWithString: here. It'll work, but the convention is to send copy messages to the objects you already have, the same as the properties would do. Send copy messages to your input string objects, then assign the copies to your instance variables.
When you do use property accesses, use the convenience constructors (stringWith…:, for example), as these return objects that you do not own. When you assign these objects to your copy-declared properties, you will actually be storing copies that you do own.
The other way would be to use alloc and initWithWhatever:, then immediately autorelease that object before assigning it to the property; this way creates an object that you own, then immediately gives up ownership before assigning it to the property.
Try
nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
compositore = [[NSString alloc] initWithString:nomeCompositore];
tipologia = [[NSString alloc] initWithString:[dicOpera objectForKey:kKeyTipologia]];
or
self.nomeCompleto = nomeOpera;
self.compositore = nomeCompositore;
self.tipologia = [dicOpera objectForKey:kKeyTipologia];
instead of self.xxx = [[yyy alloc] init...].
In the original code, the RHS of the assignment returns an object of retain count +1, and if you make the #property having (retain) or (copy), the final retain count would be +2. Therefore, even if you release these in -dealloc, the net retain count is +1, causing a memory leak.
BTW, there's no point calling +dictionaryWithDictionary:. Just use
NSDictionary* dicOpera = [[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
objectForKey:nomeCompositore]
objectForKey:nomeOpera];

Objective C /iPhone : Is it possible to re initialize an NSArray?

I read that non mutable data types can't be modified once created.(eg NSString or NSArray).
But can they be re-initialized to point to a different set of objects?
If so, do I use release to free any alloc from first time round in between uses? eg:
myArray declared as NSArray *myArray in interface, and as nonatomic/retain property.myArray set in initialization code to a point to an array of strings as follows.
self.myArray = [myString componentsSeparatedByString:#","];
But later I want to re-initialize myArray to point to a different set of strings
self.myArray = [myOtherString componentsSeparatedByString:#","];
Is it possible? Thanks...
It really depends what you mean with re-initialize. You can assign another immutable object to a pointer, because the pointers aren't constant.
Example:
#interface MyObj : NSObject {
NSString *name; // not needed in 64bit runtime AFAIK
}
#property(retain) NSString *name; // sane people use copy instead of retain
// whenever possible. Using retain can
// lead to some hard to find errors.
#end
/* ... another file ... */
MyObj *theObject = [[[MyObj alloc] init] autorelease];
theObject.name = #"Peter";
NSString *oldName = theObject.name;
NSLog(#"%#", theObject.name); // -> Peter
NSLog(#"%#", oldName); // -> Peter
theObject.name = #"Martin";
NSLog(#"%#", theObject.name) // -> Martin
NSLog(#"%#", oldName) // -> Peter
If the behavior above is what you want, that's fine.
If you want that last line to return Martin you're in trouble. Those are constant strings and are not meant to be modified. You could, if you really want, modify the memory of the object directly, but this is dangerous and not recommended. Use mutable objects if you need such behaviour.
Yes you can reinitialized the NSArray. Here is the sample code that i used to re-initialized the NSArray.
NSString *keywords = #"FirstName|LastName|Address|PhoneNumber";
NSArray *arr = [keywords componentsSeparatedByString:#"|"];
NSLog(#"First Init - %#,%#,%#,%#",[arr objectAtIndex:0],[arr objectAtIndex:1],
[arr objectAtIndex:2],[arr objectAtIndex:3]);
arr = nil;
keywords = #"First_Name|Last_Name|_Address|_PhoneNumber";
arr = [keywords componentsSeparatedByString:#"|"];
NSLog(#"Second Init - %#,%#,%#,%#",[arr objectAtIndex:0],[arr objectAtIndex:1],
[arr objectAtIndex:2],[arr objectAtIndex:3]);
Of course they can. Saying that an NSArray is immutable doesn't mean that an attribute of a class of that type cannot be changed. You can't change the content, but you can assign new content to it.
If you want to make also changing the reference impossible you should use const keyword.