I decided I need to go back over some basics recently, mostly to do with memory management and I'm beginning to doubt.
The reason I ask is because I'm a bit muddled with how to memory safe process items in array using temporary variables.
Could somebody wise in the ways of arc please tell me if this simple code will leak memory?
self.array=[NSMutableArray new];
// Retain +1
Test *obj0 = [[Test alloc] init];
// Retain +1
[self.array addObject:obj0];
Test *obj1 = nil;
//Retain +1
obj1=self.array[0];// does need to be __weak even though it has no owner?
[self.array removeAllObjects];
// is not null
NSLog(#"A: %#", obj1);
When you add obj0 to the array, its retain count will be incremented, see e.g. here. It is then +2.
When you assign it to obj1, it still has an retain count of +2.
When you remove all objects from the array, their retain count will be decremented, so obj0 has again anretain count of +1.
It thus won't leak.
Related
Here is code I am referring to.
// Person.h
#interface Person : NSObject {
NSString *firstName;
NSString *lastName;
}
#end
// Person.m
#implementation Person
- (id)init {
if (![super init]) return nil;
firstName = #"John";
lastName = #"Doe";
}
#end
// MyClass.m
#implementation MyClass
.....
- (NSArray *)getPeople {
NSMutableArray *array = [[NSMutableArray alloc] init];
int i;
for (i = 0; i < 10; i++) {
Person *p = [[Person alloc] init];
[array addObject:p];
}
return array;
}
.....
#end
Now, I know there is no memory-management going on in this sample code. What would be required?
In the getPeople loop, I am alloc'ing a Person (retainCount 1), then adding it to array. The retain count is now 2, right? If it is two, should I be [p release]'ing after adding it to the array, bringing the retainCount back down to 1?
Am I right in that it is the caller's responsibility to release the array returned by the method? (Which would also free the memory of the Person's, and their instance variables, assuming their counts are at 1).
I have read Apple's memory management document, but I guess what I am most unclear about, is what increases an objects retain count? I think I grasp the idea of who's responsibility it is to release, though. This is the fundamental rule, according to Apple:
You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message. You are responsible for relinquishing ownership of objects you own using release or autorelease. Any other time you receive an object, you must not release it.
bobDevil's sentence "only worry about the retain counts you add to the item explicitly" made it click for me. After reading the Ownership policy at Apple, essentially, the object/method that created the new object, is the one responsible for releasing /it's/ interest in it. Is this correct?
Now, let's say I a method, that receives an object, and assigns it to a instance variable. I need to retain the received object correct, as I still have an interest in it?
If any of this is incorrect, let me know.
You are correct that the retain count is 2 after adding it to an array. However, you should only worry about the retain counts you add to the item explicitly.
Retaining an object is a contract that says "I'm not done with you, don't go away." A basic rule of thumb (there are exceptions, but they are usually documented) is that you own the object when you alloc an object, or create a copy. This means you're given the object with a retain count of 1(not autoreleased). In those two cases, you should release it when you are done. Additionally, if you ever explicitly retain an object, you must release it.
So, to be specific to your example, when you create the Person, you have one retain count on it. You add it to an array (which does whatever with it, you don't care) and then you're done with the Person, so you release it:
Person *p = [[Person alloc] init]; //retain 1, for you
[array addObject:p]; //array deals with p however it wants
[p release]; //you're done, so release it
Also, as I said above, you only own the object during alloc or copy generally, so to be consistent with that on the other side of things, you should return the array autoreleased, so that the caller of the getPeople method does not own it.
return [array autorelease];
Edit:
Correct, if you create it, you must release it. If you invest interest in it (through retain) you must release it.
Retain counts are increased when you call alloc specifically, so you'll need to release that explicitly.
factory methods usually give you an autoreleased object (such as [NSMutableArray array] -- you would have to specifically retain this to keep it around for any length of time.).
As far as NSArray and NSMutableArray addObject:, someone else will have to comment. I believe that you treat a classes as black boxes in terms of how they handle their own memory management as a design pattern, so you would never explicitly release something that you have passed into NSArray. When it gets destroyed, its supposed to handle decrementing the retain count itself.
You can also get a somewhat implicit retain if you declare your ivars as properties like #property (retain) suchAndSuchIvar, and use #synthesize in your implementation. Synthesize basically creates setters and getters for you, and if you call out (retain) specifically, the setter is going to retain the object passed in to it. Its not always immediately obvious, because the setters can be structured like this:
Person fart = [[Person alloc] init];
fart.firstName = #"Josh"; // this is actually a setter, not accessing the ivar
// equivalent to [fart setFirstName: #"Josh"], such that
// retainCount++
Edit:
And as far as the memory management, as soon as you add the object to the array, you're done with it... so:
for (i = 0; i < 10; i++) {
Person *p = [[Person alloc] init];
[array addObject:p];
[p release];
}
Josh
You should generally /not/ be worried about the retain count. That's internally implemented. You should only care about whether you want to "own" an object by retaining it. In the code above, the array should own the object, not you (outside of the loop you don't even have reference to it except through the array). Because you own [[Person alloc] init], you then have to release it.
Thus
Person *p = [[Person alloc] init];
[array addObject:p];
[p release];
Also, the caller of "getPeople" should not own the array. This is the convention. You should autorelease it first.
NSMutableArray *array = [[[NSMutableArray alloc] init] autorelease];
You'll want to read Apple's documentation on memory management: http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
My original project was leaking so I searched for the leak. When I found it I created a simple new project.
The project uses ARC and the only code I added is the following.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
int elements = 10000000;
//memory usage 5,2 MB
NSMutableArray *array = [NSMutableArray arrayWithCapacity:elements];
//memory usage 81,7 MB
for (int i = 0; i < elements; i++) {
[array addObject:[NSObject new]];
}
//memory usage 234,3 MB
[array removeAllObjects];
//memory usage 234,3 MB
array = nil;
//memory usage 159,5 MB
}
After calling [array removeAllObjects] all NSObjects in the array should be deallocated and the memory usage should be 81,7 MB again.
What am I doing wrong?
Here
NSMutableArray *array = [NSMutableArray arrayWithCapacity:elements];
you are creating autoreleased object (autorelease pool).
Many programs create temporary objects that are autoreleased. These
objects add to the program’s memory footprint until the end of the
block. In many situations, allowing temporary objects to accumulate
until the end of the current event-loop iteration does not result in
excessive overhead; in some situations, however, you may create a
large number of temporary objects that add substantially to memory
footprint and that you want to dispose of more quickly. In these
latter cases, you can create your own autorelease pool block. At the
end of the block, the temporary objects are released, which typically
results in their deallocation thereby reducing the program’s memory
footprint
Wrap with #autoreleasepool {} method [NSMutableArray arrayWithCapacity:elements]:
NSMutableArray *array;
#autoreleasepool {
array = [NSMutableArray arrayWithCapacity:elements];
// [NSMutableArray arrayWithCapacity:] creates object with retainCount == 1
// and pushes it to autorelease pool
// array = some_object; usually (and in this case also) is transformed by ARC to
// [array release]; [some_object retain]; array = some_object;
// so here array will have retainCount == 2 and 1 reference in autorelease pool
} // here autorelease pool will call `release` for its objects.
// here array will have retainCount == 1
or change it to
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:elements];
You've been bitten by the dreaded autorelease pool. Essentially to make MRC (manual reference counting) manageable by people instead of releasing an object immediately it can be handed to the autorelease pool (an instance of NSAutoreleasePool, it's documentation gives more details) which will retain the object until the pool is later drained. ARC (automatic reference counting) could be designed so the autorelease machinery is not needed, but to maintain compatibility with MRC is remains.
The pool automatically drained at the end of a run loop cycle - i.e. when the application has finished processing an event. However if an application creates a lot of temporary objects and then discards them is some localised part of the program then a using a local autorelease pool can drastically reduce the maximum memory use. It is not that such temporary objects will not be release, just that they will live far longer than needed. A local pool can be create with the #autoreleasepool { ... } construct.
You can see the effect in your example by wrapping the whole of the body of applicationDidFinishLaunching:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
#autoreleasepool
{
...
}
}
and stepping through with the debugger.
In your real code you need to work back from the point which is producing lots of temporary objects to locate a suitable point to add an autorelease pool.
HTH.
Addendum
It is not the objects in your array that are not getting released when you think they should , you can test this by using a simple class which counts initialisations and deallocations, e.g.:
#interface TestObject : NSObject
+ (void) showCounts;
#end
#implementation TestObject
static uint64_t initCount = 0, deallocCount = 0;
- (id) init
{
self = [super init];
if(self) initCount++;
return self;
}
- (void) dealloc
{
deallocCount++;
}
+ (void) showCounts
{
NSLog(#"init: %llu | dealloc: %llu", initCount, deallocCount);
initCount = deallocCount = 0;
}
#end
Use this instead of NSObject and call showCounts after you are done with your test - try with/without autorelease, etc.
Your memory is always getting released, it is just the time at which it is release that is the issue. Some objects end up in an autorelease pool, either the default one which is emptied once per event, or a local one.
Unless you create a lot of temporary objects in response to a single event you normally won't see an issue. Consider whether you are chasing a real issue for your application here. If you are among the things to try to alleviate the problem are:
Avoid uses of convenience constructors of the form <name>WithX... which are shorthands for [[[C alloc] initWithX...] autorelease]. On many, but not all, occasions the compiler can remove such objects from the autorelease pool just after the convenience constructor returns (and your case appears to be one in which is can fail). The better route is to use alloc/init, new (shorthand for alloc/init) or, if provided, newWithX... (shorthand for alloc/initWithX...). Try these options with your example and see the differences in when (not if) the memory is released.
Well placed #autoreleasepool blocks.
HTH
This question already has answers here:
When to use -retainCount?
(11 answers)
Why retain count in negative value? [duplicate]
(1 answer)
Closed 10 years ago.
I have property:
#property(nonatomic, retain) NSMutableArray *myvar;
First case:
myvar = [[NSMutableArray alloc] init];
NSLog(#retainCount: %i:", [myvar retainCount]);
outputs:
retainCount: 1
Second case:
self.myvar = [[NSMutableArray alloc] init];
NSLog(#retainCount: %i:", [self.myvar retainCount]);
outputs:
retainCount: 2
My question is: why in the second case retain value is 2 ?
The only valid answer: never check, use, trust retainCount. it is not meant for debugging or use directly for memory management.
useful: whentouseretaincount.com
But in your case: As you are not using ARC (otherwise the compile wouldnt allow you to use retainCount anyway) you are over retaining.
it retain here #property(nonatomic, retain) NSMutableArray *myvar;
and here: self.myvar = [[NSMutableArray alloc] init];
do:
self.myvar = [[[NSMutableArray alloc] init] autorelease]; //will release once soon
or my favorite, as independent from ARC/MRC and short
self.myvar = [NSMutableArray array]; // identical to the first example
or more explicit
NSMutableArray *var = [[NSMutableArray alloc] init];
self.myvar = var;
[var release];
+1 because you alloc/init'd it
+1 because self.myvar retains it (as set out in your property declaration)
if you autorelease after alloc/init, it will go back down to 1... then if you set self.myvar to nil, it will hit 0 (if nothing else has retained it in the meantime)
But as vikingosegundo has said, you don't want to be messing with retain counts. The OS determines when to knock them back down, so you can't use them as a reliable measure of state.
Here's a rule that I used to go by before ARC came along:
'The NARC rule':
If you use New, Alloc, Retain or Copy, increase the count by one.
Your retain count is 2 in the second case because you are using alloc and retain on the instance property of the class (by using the self keyword). In the other case you are not using the synthesized setter to set the variable, and therefore not using retain. Explore what properties actually do for increased understanding.
vikingosegundo has explained well the correct way to do things in his answer.
Hope that helps!
See this answer: Question About Retain Counts & initializing member variables
First, you alloc init a new NSArray object. That's a retain count of
1. Second, your setter sends the object a retain message when assigning it to your instance var. That bumps the retain count up to
2.
Cheers.
I want to know that difference between following two lines
name1 = [[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement,1)] retain];
name1 = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement,1)];
What will be effect on name1 if I do use retain at the last,
I face once problem, and couldn't use name1 in a method that is being call by NSTimer, and when I use retain, they it worked fine for me.
If I do call value from database in viewDidLoad, and use in a method that is being called by NSTimer after each second, then it will give bad-exec, but when I do use retain then it will work properly,
I want to know the reason
Here is the difference
- (void)func1 {
name1 = [[NSString stringWithUTF8String:...] retain];
name2 = [NSString stringWithUTF8String:...];
}
- (void)func2 {
NSLog(#"%#", name1); //OK, name1 is still there
NSLog(#"%#", name2); //Would be crashed because name2 could be released anytime after func1 is finished.
}
I wrote this answer to another question, but it explains what you're asking:
Objects in objective c have a retain count. If this retain count is greater that 0 when the object goes out of scope (when you stop using it), it leaks.
The following things increase the retain count
[[alloc] init]
new
copy
[retain]
adding an object to an array
adding an object as a child (e.g. views)
There are likely more, but you don't appear to use any others in your code
The following decrease the retain count
[release]
removing an object from an array
if you dealloc an array, all of its objects are released
You should go through your code and ensure each of the retains or additions to an array are matched with a corresponding release. (You can release member variables in the dealloc method).
Another user made a valid point that my answer doesn't
Once you add an object to an array, it takes ownership and will release the object when it is done with it. All you need to do is make sure you release anything you own according to the memory management rules
There are also autorelease objects, have a look at this example;
-(init){
...
stagePickerArray = [[NSMutableArray alloc] init];
for (int i = 0; i < 3; i++)
{
//this string is autoreleased, you don't have call release on it.
//methods with the format [CLASS CLASSwithsomething] tend to be autorelease
NSString *s = [NSString stringWithFormat:#"%d", i);
[stagePickerArray addObject:s];
}
...
}
Your issue is that when you come to use your string later, it has a retain count of zero and has been released. By calling retain on it, you're saying 'I want to use this later'. Don't forget to match every retain with a release or you're objects will 'leak'
I bet your code wouldn't crash if your name1 was a property - either (nonatomic, retain) or just (copy) depending on your needs.
Second condition is to have name1 initialized to sth meaningful at the time your other function tries to do sth with it.
EDIT:
With a property you'd have to use synthesized setter in this case with: self.name1 = #"your string";.
Normally you don't have to manually retain/release a string created with stringWith... methods since there's nothing you created in memory yourself by using explicit alloc. Also please note that with code:
NSString *str = [NSString stringWithUTF8String:#"your string"];
your str (if not used to set a property) will stop being available when the function gets out of scope (iOS eventloop will autorelease it).
I need few clarification in iPhone memory management.
Here is an example for setters;
1).
-(void)setValue:(NSString*)input{
[value autorelease];
value = [input retain];
}
In this example, why we have to use autorelease?
Can we use like as follows?
if(value)
[value release];
value = [input retain];
In the first example, Why we should not release the memory for input
2).
If I use following statement; what is the retain count for value
NSString *value;
value = #"welcome";
After the above statement, just I am trying to set one more value. Then what will happen?
eg:
value = #"For testing";
3).
What is the different between 2) and 3)?
NSString *value;
value = [input1 retain];
...
...
value = [input2 retain];// Now what is the retain count for value
4).
If I use following statement, why the app is getting crash?
NSString *value = [[[NSString alloc] init] autorelease];
...
...
Thanks in advance..
If "input" is the exact same object as "value" then calling [value release] could dealloc the object. So you must retain the new input value, release the old value, then assign the new value to the ivar:
[input retain];
[value release];
value = input;
After each of 2) and 3), the NSString *value points to a literal NSString object, the retain count will be 1 in each case, and releasing it is probably not a good idea
After this code:
value = [input2 retain];
value is an alias to the input2 object. The thing to realize is that objects have retain counts, variables do not.
As for your last case,
NSString *value = [[[NSString alloc] init] autorelease];
It creates an autoreleased empty string. If you reference that object again once the autorelease actually happens, you may get a crash because you'll be referring to an object that doesn't exist any more.
If you release a value before you retain the new value then you can have problems if the same value is being set twice. This happens if the caller hasn't retained their own copy, such as when they get the value from the same object they try to set it on, like this:
object.value = object.value;
That statement will cause the object to be released before it's retained again which could lead to the memory being deallocated and result in a dangling pointer being retained. By doing the autorelease it ensures that copying the same pointer onto itself will work correctly.
I usually write my setters as
- (void)setValue:(NString *)input {
if (value != input) {
[value release];
value = [input retain];
}
}
which avoids the problem of input and value both being the same object. If you just release it without checking then you might completely free it and the next line will try to retain an object that doesn't exist anymore.
However, it's best practice to copy strings instead of retaining them :)
(assuming you're working in a non-gc env)
1) you can create a temporary variable and release the temp after the retain/assign. otherwise, you'd need to compare the pointers. deferred release may also mask threading errors (whether you consider that good or bad is...)
2) technically, the NSString literals are valid for the life of your program. that is: while (1) [#"why won't i die?" release]; yields an infinite loop.
3) with explicit retain, alloc+init, new, and copy, you must counterbalance the retain count using release or autorelease. since you did not release value1, the static analysis may (correctly) spot that as a leak. since the string constants never die, the two aren't comparable in this regard.
4) there's nothing wrong with that staement. the problem lies elsewhere in your program. either you are assigning it to an ivar without retaining, or releasing it later.
try using static analysis often, and try reducing how much you use autorelease (you can't avoid it entirely).
none of this is magic, but you can at least reduce the location of many problems to the callsites (or very close) by not using autorelease all over the place. just use manual retain/release where possible.
lastly, check for leaks and run with NSZombies enabled.
1)
You will have to do autorelease because of the following:
When input is the same object as value and you will release value it's retain count will reach zero and get deallocated before you can retain it again though the input.
You can do it with retain but you have to change your code:
-(void)setValue:(NSString*)input{
if (value != input) {
[value autorelease];
value = [input retain];
}
}
2)
I believe #"Text" will be treated as a constant. When you want a object which you do not want any memory management with use:
NSString *value = [NSString stringWithString:#"Text"];
This will return an autoreleased object.
3) In this example it is not about the retain count of value, but about the retain count of both objects where value one is referenced to.
When you dont release input1 before you leave that method, you will have a memory management problem.
4) This statement should work. No points to argue. You rather use [NSString string].
Note:
For memory management: when you use alloc new or copy, you also have to use release or autorelease on the same object in the same scope.