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.
Related
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.
Lets say I have local variable(not property) Obj *x = d, is d's reference count incremented? Or is it default a weak reference?
Apple's documentation (Variable Qualifiers section) said:
https://developer.apple.com/library/ios/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
__strong is the default. An object remains “alive” as long as there is a strong pointer to it.
Say you write
__weak NSMutableArray* myArray = [[NSMutableArray alloc] init];
What happens? What happens is that the only reference to that array is in a weak variable, which means it gets deallocated immediately and myArray is set to nil. Now say you write
NSMutableArray* myArray = [[NSMutableArray alloc] init];
What would happen if the default is "weak"? Does that answer your question?
They are strong by default.
docs
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
In my iOS app ARC is disabled. I find some memory leak issues in my code when i inspect my code using instrument tool, it is described bellow.
Obj1 is a an object that declared in the .h file (its type is classA).
I set the value of Obj1 in .m file using the code:
self.Obj1 = [[classA alloc]init];
I release Obj1 in the dealloc method.
this code works fine. but shows a memory leak issue.
How can i avoid this memory leak issue.??
Use below code :
self.Obj1 = [[[classA alloc]init]autorelease];
Dealloc will only be called when the view is not in use or unload.
If you use NSZombieEnabled macros for handling work with released objects all objects (even released) will have at least 1 retain count and tools will show all object as "leaking". Just turn off this macro when you work with memory leaks.
See technote for references:
https://developer.apple.com/library/ios/technotes/tn2239/_index.html
If you make a property retain Obj1 means increment by 1 then after you alloc this object by increment 1, so it's value retain 2.
Use below code:
ClassA *objC = [[classA alloc]init];
self.Obj1 = objC;
[objC release];
There are many ways to handle this :
First,
self.Obj1 = [[[classA alloc]init]autorelease];
Second,
Obj1 = [[classA alloc]init];
...
// use self.Obj1 in the code
...
[Obj1 release];
Third,
self.Obj1 = [[classA alloc]init];
...
[self.Obj1 release];
Use any one out of these.
You can remove self., just Obj1 = [[classA alloc]init];.
Because when you call self.,you retain the object again.So after self.Obj1 = [[classA alloc]init];,object retain count is 2.
I don't understand this one unless it's because I'm releasing the property instead of the ivar. Can someone shed light on the problem?
self.dataToBeLoaded = [[NSMutableData alloc] initWithLength:10000];
[self.dataToBeLoaded release];
The warning is Incorrect decrement of the reference count of an object that is not owned by the caller.
The dataToBeLoaded property has the retain attribute associated with its setter.
My understanding is the the alloc init increments the retain count and the property assignment increments the retain count. Since I only one to retain it once, that's why I release it immediately after the assignment.
UPDATE -- some experimental results:
Since I noted in my comments below that I have received contradictory advice on what the retain property does to the synthesized setter, I thought I would do a little experiment using the code above, modified with some logging:
NSLog(#"retain 1 = %d", [dataToBeLoaded_ retainCount]);
self.dataToBeLoaded = [[NSMutableData alloc] initWithLength:10000];
NSLog(#"retain 2 = %d", [dataToBeLoaded_ retainCount]);
[self.dataToBeLoaded release];
NSLog(#"retain 3 = %d", [dataToBeLoaded_ retainCount]);
The results at each log statement were 0, 2, and 1.
Apparently, it's not possible to step into the alloc or the init code to see the retain count go from 0 to 1 to 2. I could have subclassed the NSMutableData class, but I was short on time.
I know a lot is said that you can't rely on the value of the retainCount property, but what I have seems consistent and I would expect reasonable behavior over the short scope of the code like that shown in the example. So I'm inclined to believe that prior advice is correct -- the retain property is a promise to include a retain within the setter. So here I have the retain from the alloc/init and the retain from the call to the setter. Hence, the retain count is set to 2.
When I run this code:
NSMutableData *theData;
NSLog(#"retain 1 = %d", [theData retainCount]);
theData= [[NSMutableData alloc] initWithLength:10000];
NSLog(#"retain 1a = %d", [theData retainCount]);
self.dataToBeLoaded = theData;
NSLog(#"retain 2 = %d", [theData retainCount]);
[self.dataToBeLoaded release];
NSLog(#"retain 3 = %d", [theData retainCount]);
The retain count at each log statement is 0, 1, 2, 1.
So I have evidence that suggests the setter is providing a retain. This appear to be more of a promise than a hint, because it is actually happening.
I'm open to other explanations. I don't want to be arrogant about this. I just want to get it right as to what is happening. I appears that the warning (in the subject of this question) is really spurious and not something to worry about.
One more experiment is done using assign rather than retain as an attribute in the #property statement. With the same code:
NSMutableData *theData;
NSLog(#"retain 1 = %d", [theData retainCount]);
theData= [[NSMutableData alloc] initWithLength:10000];
NSLog(#"retain 1a = %d", [theData retainCount]);
self.dataToBeLoaded = theData;
NSLog(#"retain 2 = %d", [theData retainCount]);
[self.dataToBeLoaded release];
NSLog(#"retain 3 = %d", [theData retainCount]);
The retain count at each log is 0, 1, 1 (the setter did not retain), then the error message: message sent to deallocated instance. The last release had set the retain count to zero, which triggered the deallocation.
UPDATE 2
A final update -- when the synthesized setter is overridden with your own code, the retain attribute is no longer observed unless your setter explicitly includes it. Apparently (and this contradicts what I had been told in other threads here) you have to include your own retain in the setter if that's what you want. While I didn't test it here, you probably need to release the old instance first, or it will be leaked.
This custom setter no longer has the property attributes of the #propety declaration:
- (void) setDataToBeLoaded:(NSMutableData *)dataToBeLoaded {
dataToBeLoaded_ = dataToBeLoaded;
}
This makes sense. Override a synthesized setter and you override all of the declared properties. Use a synthesized setter, and the declared properties are observed in the synthesized implementation.
The #property attributes represent a "promise" as to how the synthesized setter is implemented. Once you write a custom setter, you're on your own.
The key is to think through what the below code is doing. I'll write it out in full for clarity:
[self setDataToBeLoaded:[[NSMutableData alloc] initWithLength:10000]];
This creates an object with a +1 retain count and passes it to setDataToBeLoaded:. (*) It then throws away its reference to that object, leaking it.
[[self dataToBeLoaded] release];
This calls dataToBeLoaded and releases the object returned. There is no promise whatsoever that the object returned by dataToBeLoaded is the same as the object passed to setDataToBeLoaded:. You probably think they're the same, and looking at your code you can probably convince yourself that it will always work out that way, but that's not an API promise.
The code posted by Antwan is correct:
NSMutableData *data = [[NSMutableData alloc] initWithLength:1000];
self.dataToBeLoaded = data;
[data release];
That creates an object with a +1 retain count. Then passes it to a method, then releases it.
Or, if you're willing to use the autorelease pool, you can simplify it to:
self.dataToBeLoaded = [NSMutableData dataWithLength:1000];
(*) Technically this passes a message to self that may or may not cause this method to be called, but that muddies the issue. For most purposes, pretend it's a method call. But do not pretend that it just sets the property. It really is going to call some method.
EDIT:
Maybe this code will make the issue a little clearer. It's indicative of common caching solutions:
.h
#interface MYObject : NSObject
#property (nonatomic, readwrite, strong) NSString *stuff;
#end
.m
#interface MYObject ()
#property (nonatomic, readwrite, weak) MYStuffManager *manager;
#implementation MYObject
... Initialize manager ...
- (NSString*)stuff {
return [self.manager stuffForObject:self];
}
- (void)setStuff:(NSString *)stuff {
[self.manager setStuff:stuff forObject:self];
}
Now maybe manager does some foolery in the background. Maybe it caches various copies of stuff. Maybe it copies them. Maybe it wraps them into other objects. What's important is that you can't rely on -stuff always returning the same object you passed to -setStuff:. So you certainly shouldn't release it.
Note that nothing in the header indicates this, and nothing should. It's not the callers' business. But if the caller releases the result of -stuff, then you will get hard-to-debug crashes.
#synthesize is just a shorthand for writing some tedious code (code that implements stuff and setStuff: as reading and writing an ivar). But nothing says that you have to use #synthesize for your properties.
My guess would be that the method
- (NSMutableData *)dataToBeLoaded;
does not contain any of the memory management keywords therefore it is assumed that you do not own the data returned and therefore should not be releasing it.
Either use
NSMutableData *data = [[NSMutableData alloc] initWithLength:1000];
self.dataToBeLoaded = data;
[data release]; data = nil;
or if you can why not lazy load it when you actually need it?
- (NSMutableData *)dataToBeLoaded;
{
if (!_dataToBeLoaded) {
_dataToBeLoaded = [[NSMutableData alloc] initWithLength:1000];
}
return _dataToBeLoaded;
}
It just means that you are releasing an object you don't own.
I'd say call it with the instance var directly instead of using the getter, but not sure whether that will fix your analyses warnings. Also why not use [NSMutableData dataWithLength:1000]; which is autoreleased and therefore eliminates the need of that extra release call ( and would probably get rid of that warning too! )
other ways you could fix it:
NSMutableData *data = [[NSMutableData alloc] initWithLength:1000];
self.databToBeLoaded = data;
[data release];
I provided a couple of updates that I think answer what is going on here. With some test results, my conclusion is that this warning is spurious, meaning it does not really identify improper code. The updates should speak for themselves. They are given above.