What is the right way to get an NSString object from an NSArray, keeping Memory Managment in mind? - iphone

What is the right way to get an NSString object from an NSArray, keeping Memory Managment in mind.
Suppose I have an array
NSArray *myNewArray = [[NSArray alloc] initWithObjects:.......];
Now I want to get an object from this NSArray at index 2.
NSString *nameString = [myNewArray objectAtIndex:2]; // is it the right way? how to deal with "nameString"
// now regarding memory managment, should I release it ?
OR I should first alloc nameString and then assign value to it ?

Check out the Cocoa Memory Management rules (how many times have I started a post with that sentence?). In particular
You only release or autorelease objects you own.
You take ownership of an object if you create it using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message.
You use release or autorelease to relinquish ownership of an object. autorelease just means “send a release message in the future” (specifically: when the used autorelease pool receives a drain message—to understand when this will be, see “Autorelease Pools”).
Does the method objectAtIndex: begin with "alloc"? No. Does it begin with "new"? No. Does it begin with "copy" or "mutableCopy"? No. Have you sent retain to the returned object? No.
Therefore you do not own nameString. Therefore you must not release or auto release it.
Sorry if the above seems a bit "leading by the nose" but when I first started with Objective-C, I found it useful to pretty much go through all the above in my head in exactly that way, otherwise I tended to get it wrong. It doesn't take long for it all to become second nature.

I think you don't totally get the "pointer" concept yet. Your variable nameString is just a pointer. Not a string.
In the line :
NSString *nameString = [myNewArray objectAtIndex:2];
You just assign the pointer to the actual memory address of the second object of the array. That's all.
If you are about to keep this object alive (even if the array will be deallocted), you better retain that object.

NSArray *myNewArray = [[NSArray alloc] initWithObjects:.......];
NSString *nameString = [myNewArray objectAtIndex:2]
[myArray release];
That's it, no need to bother any more with it. NSString will be released by environment itself.

There is no need to allocate or initialize NSString object and after all if you are not allocating any memory then there is no need to release....Only release NSArray objects nothing else....
You are going through right way...

There is no need to release the nameString.You only release when you are using(alloc”, “new”, “copy”, or “mutableCopy”) .

NSArray *myNewArray = [[NSArray alloc] initWithObjects:.......];
and getting value in NSString from any of index wont affect memory ...no need to allocate here.
The collected NSString need not required to be retained until you are passing its ownership to another controller/object.

Related

objective-c: when to autorelease an nsnumber object when not using alloc

I noticed that my program was crashing because it was running out of memory. I figured out that this was happening because of this code segment:
DataSet *tempSet = [[DataSet alloc] init];
tempSet.rightFoot = [NSNumber numberWithDouble:temp1];
tempSet.leftFoot = [NSNumber numberWithDouble:temp2];
[footData addObject:tempSet]; //add dataSet object to the array
[tempSet release];
I read some tutorials about memory management online and was able to figure out that I needed to do this: (notice the added "autoreleases")
DataSet *tempSet = [[DataSet alloc] init];
tempSet.rightFoot = [[NSNumber numberWithDouble:temp1] autorelease];
tempSet.leftFoot = [[NSNumber numberWithDouble:temp2] autorelease];
[footData addObject:tempSet]; //add dataSet object to the array
[tempSet release];
I am still confused about why I had to do this. I did not use alloc, new or copy when creating the numberWithDouble.
Does this mean that I would need to add autorelease in this situation as well?:
[subset addObject:[NSNumber numberWithDouble:temp]];
What about this situation?:
tempSet.rightFoot = [NSString stringWithString:#"temp"];
I appreciate any help.
+numberWithDouble
is called a convenience method. Meaning, it replaces the little section of code that would look like this:
[[[NSNumber alloc]initWithDouble:double]autorelease];
Most (if not all) convenience methods are auto release by default, so the OP code with the autoreleases is incorrect, as it drops the retain count to -1.
The equals sign however is equivalent to
[self setRightFoot:[[[NSString alloc]initWithString]autorelease]];
which increments rightFoot's retain count and requires it to be released elsewhere.
as for the -addObject code, it returns void, so it does not in fact increment the receiver's retain count, and requires no release of the receiver. The object in the array should already be released by the convenience method for later, which doesn't matter because the array is now holding "copy" of it.
This is not an answer (I just do not know how to comment -- I only see "share, edit, flag"), but just a few info about Memory Management in iOS:
1. Don't release objects that you do not own.. ---> owned objects are usually the ones you "alloc", "new", "copy." //And probably the one in your #property wherein you "retain" an object.
2. when you "autorelease" an object, don't "release" it afterwards, because that would mean you're releasing the same object twice.
But there's ARC already, so you better upgrade your Xcode to avoid overreleasing objects / memory leaks (for not releasing objects)..
If there's something wrong or inappropriate with the one I put here, please edit. :)

Understanding memory management in ios

I am in the process of learnig objective-c and programming an iPad app. One thing I keep tripping myself up on and having to re-read is memory management. I am getting there...slowly. Basic rules such as for every alloc / retain you must have a release is useful. However, one relatively basic thing eludes me and I wonder if someone could explain...
Take the following code...
NSArray *myArray = [[NSArray alloc] init];
myArray = [someNSSet allObjects];
This is relatively straight forward coding and would require a [myArray release] statement.
However, I keep seeing examples of (and indeed, I have used extensively the following 'short cut'...
NSArray *myArray = (NSArray *)[someNSSet allObjects];
How, as far as I understand when you use the (NSString *) you dont need to use a [myArray release] statement, but I dont understand why.
Could someone possible explain?
NSArray *myArray = [[NSArray alloc] init];
myArray = [someNSSet allObjects];
this code is leaking myArray because you lose the reference to NSArray that you've allocated on the first line; you don't need to alloc here, because on the second line you're assigning a new value to myArray.
NSArray *myArray = (NSArray *)[someNSSet allObjects];
and this code example is perfectly fine, you're assigning the result of [someNSSet allObjects] to myArray pointer and you don't own the returned value, so you don't need to care about releasing it.
Consider using ARC (Automatic Retain Counting) for you project. With ARC the compiler takes care of retain counts so you don't have to, in fact aren't allowed to. There is a refactoring that will convert a current project.
As you said, there is a leak in the first code you posted. so you must add a release:
NSArray *myArray = [[NSArray alloc] init];
[myArray release];
myArray = [someNSSet allObjects];
In fact, when you obtain an object through a method that starts with alloc, new or copy, you own it, and you should release it. That's why, here you should release the array you obtained using the method alloc. This convention makes it easy to know when you own objects and when you don't. So remember: alloc, new or copy.
As for the second example, you obtained the array though a method that doesn't start with one of the three words (alloc, new or copy), so you don't own the object, and you are not responsible of releasing it. In fact, the array you obtained is an autoreleased object, which means that even though its retain count is currently 1, it will be automatically released when something called the autorelease pool is drained.
Here is a reference about Memory Management Rules.
In the first line:
NSArray *myArray = [[NSArray alloc] init]
some amount of memory is allocated for an array (actually in this case it is senseless since the size of the array is 0. Keep in mind that NSArray is immutable!).
The variable myArray holds the address of the first byte of the reserved memory area.
Now in the second line you change the value of myArray which now will point to the first byte of the memory area where [someNSSet allObjects] is stored. At this moment you do not know any more where the array is stored what you've created in the first line. And so you have a leak.
The line:
NSArray *myArray = (NSArray *)[someNSSet allObjects];
is correct, since you do not reserve any memory at this point. If you are not using ARC you might call retain in order to keep GC away from the referenced block of memory. In other way you might receive a BAD_EXEC when the owner of the object releases it and you try to access it through e.g.: [myArray objectAtIndex:0]

iPhone memory management with NSString problem

#property (nonatomic,copy) NSString *orginalString;
..
NSString *tmpString =[[NSString alloc] init];
self.orginalString=tmpString;
[tmpString release];
NSString *newString =self.orginalString;
What happens here to newString?, is this correct what I am doing?
orginalString retain count 1 at first, and when it is referenced with another pointer "newString" its retain count will be 2? do I need to say "self.orginalString=nil" in the end? there are serious memory leaks but dont know it is something related with this.
NSString *tmpString =[[NSString alloc] init];
tmpString is allocated and initialized
self.orginalString=tmpString;
the string pointed to by tmpString is copied over to self.originalString (because the property is declared copy);
[tmpString release];
the string pointed to by tmpString is released, correctly; nothing happens to the string pointed to by self.orginalString;
NSString *newString =self.orginalString;
a new pointer is created and initalized to point at the same string pointed to by self.orginalString; nothing happens to self.orginalString retain count; it's just a second pointer pointing to the same object;
at this point, if you don't release somewhere self.orginalString, it will be leaked.
When you are dealing with memory management in OBjective C, my suggestion is not try and reason in terms of "retain count"; retain count is just the mechanism that is used by the ObjC runtime to keep track of objects; it is too low-level and there are too many other objects around to increase or decrease the retain count, so that you immediately loose count.
The best way, IMO, is reasoning in terms of ownership: when an object wants ownership of another, it will send a retain; when it has done with it, it sends release. Ownership is a local concept to a class, so it is easy to track down.
So, when you do:
NSString *newString =self.orginalString;
newString is just a pointer to a not-owned object; you do not need to balance that assignment with a release; on the contrary, if you do:
NSString *newString = [self.orginalString retain];
you are making yourself responsible for releasing the object when you have done with it.
You should visit this link. Actually, we should not check memory leaks with retain count, atleast for NSString.
To check memory leaks, always use Instruments coming with Xcode.

NSDictionary leak on iPhone and object reference graph

I am simply loading a plist into a temporary dictionary for initializing my data model. Unfortunately this single line of code below results in consistent leak throughout the app life cycle as per Instruments. The leaked objects being NSCFString and GeneralBlock on a Malloc and the responsible library being Foundations.
My two questions for the experienced eyes:
Am I doing something strange to trigger this ? I tried surrounding it in autorelease block but it had no effect.
Is there a way to print list of object references of a suspect object to get insight into the object orphaning process.
Leaking Line:
NSDictionary *tempDict = [NSDictionary dictionaryWithContentsOfFile:
[[NSBundle mainBundle]
pathForResource:resourceName
ofType:#"plist"]];
totalChapters = [[tempDict objectForKey:#"NumberOfChapters"] intValue];
chapterList = [[NSMutableArray alloc] initWithCapacity: totalChapters];
[chapterList addObjectsFromArray:[tempDict objectForKey:#"Chapters"]];
It appears you may be leaking at this line:
[[NSMutableArray alloc] initWithCapacity: totalChapters];
If that object isn't being released, then any objects you add to it won't be released either
Edit (because it's too long for a comment):
Instruments tells you where the memory was allocated, but not why it is still being retained. When NSDictionary loads the contents of a file, it has to create an object for each element it loads. If one later retrieves an object using objectForKey:, retain it and forget to release, a leak will be reported. The dictionaryWithContentsOfFile statement will be blamed for it because it performed the allocation.
I concur with Don's phantom debugging. Most likely you haven't released the old chapterList when you assign to it the second time.
I am releasing all the allocated stuff in the dealloc method of this class. The TROUBLED line below gets repeatedly called in each alloc init to help me load new chapters based on user selection.
NSDictionary *tempDict = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:resourceName ofType:#"plist"]];
Since I am using convenience method on NSDictionary I don't have anything to Dealloc. But Instrument points to this particular line as the source of leak via a Malloc.
tempDict is a just a scratch space for a dictionary to load a plist and init the ivars.
The autorelease block only effects objects placed into the autorelease pool. So tempDict is place in the autorelease pool but totalChapters is not. If you would like to use the autorelease pool then you either want:
[[[NSMutableArray alloc] initWithCapacity: totalChapters] autorelease];
or don't use the autorelease pool and use:
[[NSMutableArray alloc] initWithCapacity: totalChapters]
and then
[totalChapters release]
Avoiding the autorelease pool is recommended unless necessary.

Sometimes NSString becomes Invalid?

In my own iPhone Application I have used a number of nsstring variables to store values. But sometimes its value becomes Invalid! Does anybody know what may be the reason? Or tell me about the situation when a nsstring variable becomes Invalid?
NSStrings are also objects. They go by the same memory management rules. If you sin against those rules you will get a pointer to invalid memory and it will look like "invalid" data.
For example:
myString = [NSString stringWithString:#"foo"];
This will create an NSString* that's got autorelease on. When you're assigning it to an ivar that autorelease pool will soon pop putting the retain count back to 0 and dealloc'ing it while you still have a reference to it!
Naughty.
Instead, either retain it or use:
myString = [[NSString alloc] initWithString:#""];
This returns an owning reference. Remember to [myString release]; in dealloc.
This is just one example/case of bad memory management. Read the docs on how to properly manage your object lifecycle. Really.
They are being autoreleased. If you create a string using a convenience method (stringWithString, stringWithFormat) it will be added to the autorelease pool.
To stop it being released automatically, sent it a retain message and a release message when you have finished using it so that the memory is released. You can also set up properties to do this semi automatically when you assign the string to a member variable
There are a lot of articles on iPhone (and Mac) memory management here on SO and on the interwebs. Google for autorelease.
If this is for a device with fairly limited resources such as the iPhone, you should use [[NSString alloc] init*] over the static convenience methods, as you will put less pressure on the autorelease pool and lower your memory usage. The pool is drained every message loop, so the less objects to enumerate the better.
You should also be aware that autorelease objects in a loop will generate a lot of garbage unless you manage a local autorelease pool.
did you copy or retain the NSString ?
I asked the same question. And the following answer was the best of convenience. Just use:
[myString retain];
And then in dealloc method (e.g. viewDidUnload):
[myString release];