retain will allocate Memory in RAM? - iphone

the follwing will allocate memory in RAM ....?
NSArray *obj = [[NSArray arrayWithObjects:
#"Hai", #"iHow",
nil] retain];

Yes. It will create an NSArray object and store it on the heap. The arrayWithObject method returns an autoreleased object, but your extra retain statement makes sure the reference count will be at least one, and the memory won't get freed until you explicitly release it.
It might be worth adding, it's not the "retain" statement that allocates the memory, the memory is allocated by the arrayWithObject method. The retain statement simply increments the reference count for that object.

To add to Tom's correct answer, the line:
[NSArray arrayWithObjects: ...]
is equivalent to:
[[[NSArray alloc] initWithObjects:...] autorelease]
so rather than tacking a retain onto the first line, you could just do:
[[NSArray alloc] initWithObjects:...]
In any case, the memory is allocated in alloc, whether that method appears in your code or is implicit (as it is in autoreleasing class-method convenience calls like the first one).

Related

Strange Memory leak

I have been working with iPhone application in which i am using sqlite database. I have checked memory leak in iPad,iPhone 4 and there is no memory leaks. But same code show memory leak in iPhone-3GS. Please take a look at below scree-shot and i think there is no leak than after instruments showing me leaks. Please do advice me.
Thanks in Advance.
Its actually not that strange, see how you allocate memory for both Query and Query1 via [[NSArray alloc] init. But then, you overwrite the pointer of them by calling [database executeQuery:], so the allocated memory is leaked.
It looks like you don't really need the array allocation anyway, so you can simply remove that line. However, that the [Query release] and [Query1 release] doesn't crash the app (or at least at some later point), indicates that [database executeQuery:] return retained memory and thus delegates its ownership to the caller, which is discouraged by the memory guidelines. You should return an autoreleased object from executeQuery: and because you never use the returned value, you can simply reduce the method down to:
[self databaseOpen];
[database executeQuery:#"Delete from ActivityList"];
[database executeQuery:#"Select* from ActivityList"];
The problem is this:
NSArray *Query = [[NSArray alloc] init];
// later:
Query = [database executeQuery:str];
Thusly, you allocate an NSArray, then reuse its pointer, so you loose the reference to the firstly allocated object. You don't need to do
NSArray *Query = [[NSArray alloc] init];
as the executeQuery: method returns an initialized array.
The same holds to Query1 as well (you're also allocating it erronously).
(By the way, are you using my SQLHelper library? If so, you must not release the array returned by the query; it's autoreleased and will crash upon overreleasing)
That's because you're allocating NSArray for Query and Query1 but never use them, you immediately change the pointer to something else which is your [xxxx executeQuery:xxx].
Change them to these and it should be fine.
NSArray* Query = [database executeQuery:str];
NSArray* Query1 = [database executeQuery:str1];

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]

regarding use of nonatomic and retain property

I've a NSMutableArray in AppDelegate
I'm using property (nonatomic,retain)
and synthesizing it
again in didfinishlaunch allocating it using [[nsmutablearray alloc]init];
So, my doubt is if I'm releasing it in deallloc using release method.
is it released properly?
or still retain count is there.
if I'm doing wrong kindly provide a proper solution.
It depends on how you're assigning it. If your assignment is directly to the ivar, like
myProperty = [[NSMutableArray alloc] init];
Then a single release in dealloc is adequate, because you have an expected retain count of 1 from the alloc.
On the other hand, if you have used a synthesized setter via either:
[self setMyProperty:[[NSMutableArray alloc] init]];
or
self.myProperty = [[NSMutableArray alloc] init];
then you have almost certainly leaked the object. You incremented the retain count twice (once via alloc and once in the setter) and only decremented it once (in dealloc).
Best IMO is to use the setter and the autorelease pool:
self.myProperty = [[[NSMutableArray alloc] init] autorelease];
Here the alloc is balanced with a local autorelease, and the setter`s retain is balanced with the dealloc release.
While that approach involves two extra methods (the setter method and the autorelease call), it ensures that any retained values that were formerly set in the the property are released as necessary (in the setter method).
Yes, you still need to release it in dealloc.
These two pages are must-reads for iOS/Cocoa developers regarding memory management
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-BAJHFBGH
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html
For your specific example, see the section on the second link called "Implement dealloc to Relinquish Ownership of Objects"
Here is also a link to this issue addressed on Stack Overflow:
Understanding reference counting with Cocoa and Objective-C
If you do something like:
my_property = [[NSMutableArray alloc] init];
The 'my_property' assumes ownership of your array. Your class's dealloc method has to release the array to prevent leaks.
[my_property release];
-or-
self.my_property = nil;
If you are using
[self setMutableArray:[[nsmutablearray alloc] init]];
in this case it will release any memory which is previously assigned to it(give up ownership).
so in dealloc method you are simply
writing
self.mutableArray = nil;
then it will give up the ownership and memory allocated to mutableArray will be released automatically
In your case the retain count will be 2 and the array keeps releasing. When you have set the property you do not need to initialise it .

TablewView example in Beginning iPhone book

This is probably a dumb question, but in the introduction to TableViews, the author has a property of NSArray *listData to fill the table with dummy data. In the viewDidLoad, he basically does this:
- (void)viewDidLoad {
NSArray *array = [[NSArray alloc] initWithObjects#"1", #"2", #"3", more stuff, nil];
self.listData = array;
[array release];
...
}
Why does he create another array and set it to the property as opposed to doing something like
- (void)viewDidLoad {
listData = [[[NSArray alloc] initWithObjects#"1", #"2", #"3", more stuff, nil]autorelease];
Is it to manage memory better by using alloc/init vs the autorelease pool? Or is the second way just not going to work? Thanks.
Your code is wrong and is likely to crash. In his code, he calls alloc, meaning the retain count is 1. He then assigns it to a property. I'm assuming this property is declared to be retain, in which case the retain count would go up to 2. He then called release, which drops the retain count back to 1.
In your code, you call alloc, meaning the retain count is 1, then you call autorelease, which means that the retain count will drop to 0 and the object's memory will be deallocated soon. You assign the object to an instance variable - not a retained property like he does - so you won't increase the retain count any more. This means that you'll be left with a dangling pointer to memory that could be overwritten with anything else at any time. When you try to access listData, you'll crash because it is likely to have been overwritten.
Please read Memory Management Programming Guide if you do not understand what is happening here.
Having said that, the core of your question is valid. There's nothing stopping him from doing the same as you, except assigning to a retained property instead of an instance variable.

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.