I am new to iOS and objective-C and although I've been struggling for a while to understand memory management I am disappointed in myself because I still have to get the full picture... My problem is that I do not understand how retaining a property of an object relates with retaining the whole object. Let's take the following code as an example:
#interface TestObject:NSObject { //TestObject declaration
NSNumber *firstNumber;
}
#property (nonatomic, retain) NSNumber *firstNumber;
#end
#synthesize firstNumber;
-(void) dealloc //Use standard synthesized getter and setter, write only custom
//dealloc
{
[firstNumber release];
}
...and the following code that uses it:
-(IBAction) runClicked: (id) sender
{
TestObject *to1=[[TestObject alloc ] init];
to1.firstNumber=[NSNumber numberWithInt:10]; //retain count 1 on firstnumber
NSNumber *num=[to1.firstNumber retain]; //retain count 2 on firstnumber
[to1 release]; //retain count 1 on firstnumber because of 1 release in dealloc
}
I ran an analyze on the code and also ran the program with Leak instrument and no leaks were found by either. Isn't there a leak on firstnumber (accessible by num after main object release) since the number will not be usable by any pointer after *num is also destroyed at the end of function body?
Thank you so much for your time!
Best regards,
Florin.
No, there isn't a leak as first number is an autoreleased object.
Related
I used to do this till once I found the retain count of one of my retained propery is zero before dealloc function. (This situation is normal or abnormal?)
NOTE: It's a RC condition, not ARC.
For example, I got 4 retained properties below, should they always be released in dealloc function?
If not, how could I know when to release, and when not to release? Manually judge the retainCount?
#property (nonatomic, retain) NSString *fileName;
#property (nonatomic, retain) UIImage *fullSizeImage;
#property (nonatomic, retain) UIImage *thumbnailImage;
#property (nonatomic, retain) UIImageView *checkedImageView;
- (void)dealloc {
[checkedImageView release];
checkedImageView = nil;
[fileName release];
fileName = nil;
[fullSizeImage release];
fullSizeImage = nil;
[thumbnailImage release];
thumbnailImage = nil;
[super dealloc];
}
Well, if the question is "always?", then Wain is almost right...
a SHORT answer is YES...
because in general, when someone set-up a property, it means he's going to use it as a property, that is he uses its setter method to initialize it.
BUT (LONG answer): NO, NOT ALWAYS:
what if you, somewhere in your code, initialize the private var associated to the property without it's setter method? Keep in mind that a property is not a var, but just a useful way to get methods from Xcode to get and set a var associated to it.
in other words, when you write in .h:
#property (nonatomic, retain) NSString *fileName;
and in .m:
#synthesize fileName;
you are declaring a var called fileName and are asking xcode to create 2 (invisible) methods for you:
a setter, used to set a new retained value in fileName:
-(void)setFileName:(NSString *)newString{
if (fileName == newString) {
return;
}
NSString *oldString = fileName;
fileName = [newString retain];
[oldString release];
}
and a getter, used to get the value of fileName:
-(NSString)fileName{
return fileName
}
so, when you somewhere in your code use:
self.fileName = #"ciao";
you are using the property setter method, exactly as if you'd call it directly (and you can do it, the invisible method setFileName: really exist):
[self setFileName:#"ciao"];
doing so, as you can see in the setter method, from now on fileName is retained, and so you should release it in dealloc.
BUT, to answer your question:
if you use the dot rule to set a new string in your var, ok, everything is fine,
but you may decide to set it in the standard way, somewhere, maybe just for mistake:
fileName = #"ciao";
// code
fileName = #"Hallo";
// code
fileName = #"Bye";
this way you are not using the property setter method, but you are using the var directly, and so fileName is not retained, and if you try to release it, well you may get a crash...
PS:
Manually judge the retainCount?
no, never do that
Yes, they should always be released in dealloc. If you get to dealloc and something is already released and not set to nil then you did something wrong with your memory management elsewhere in the app.
Technically in dealloc you don't need to set to nil after releasing but setting to nil after releasing is a generally good idea.
Your dealloc is unnecessarily calling the getter for each property and then immediately releasing it. Just assign nil to release the properties:
- (void)dealloc {
self.checkedImageView = nil;
self.fileName = nil;
self.fullSizeImage = nil;
self.thumbnailImage = nil;
[super dealloc];
}
Although if you are following the current trend of letting clang auto-generate your backing instance variables, then this is better, as it won't cause KVO side-effects:
- (void)dealloc {
[_checkedImageView release];
[_fileName release];
[_fullSizeImage release];
[_thumbnailImage release];
[super dealloc];
}
Yes, they should normally all be released. If you have a retain count of zero, that usually means you've made a mistake somewhere in your memory management code.
You ask: If not, how could I know when to release, and when not to release? Manually judge the retainCount?
Possibly, but you could also let Xcode help you, using static analysis. Go to Product -> Analyze. It will quite often help you find erroneous releases, etc.
When to release? Quite obviously, if your object was holding a reference to another object, and your object goes away, then it should stop holding a reference to the other object. Why would you even look at the retain count? Retain count is about other people holding on the same object, but they are none of your business. They should know what they are doing. So you release the object. You do your job; everyone else has to do theirs. The easiest way, as others said, is to assign
self.someproperty = nil;
If your object was the only one holding a reference, that other object will go away. If others held a reference, it won't go away. Just as everyone would expect. The "release" method should be the only one ever caring about what the retain count of an object is.
my.h file
#interface myObject : NSObject {
NSMutableDictionary *myDictn ;
}
i have a property
#property (nonatomic,retain) NSMutableDictionary *myDictn ;
then in .m File
i have a allocated it from id
- (id)init {
if (self=[super init]) {
myDictn= [NSMutableDictionary alloc]init];
}
}
my Question is i have mentioned Retain in Property and i have allocated memory also (is Retain Count goes to 2 in (id)init )
so how to manage memory in this case ?
i am new to this so dnt have much idea regarding memory management .
one more thing if i have a method x in my code and i also allocated memory to myDictn then in that case also how can i use release .??
Thanks in Advance .
The proper way to initialize a retained property is:
NSMutableDictionary *newDict = [[NSMutableDictionary alloc] init];
self.myDictn = newDict;
[newDict release];
or alternatively:
self.myDict = [NSMutableDictionary dictionary];
The first method makes sure not to increase the already retained property's retain count by performing alloc,init (an action that increments retain count) on a temporary object.
The second method uses a convenience method for obtaining an autoreleased instance of the dictionary.
You should first refer to Cocoa Fundamentals Guide. Then, if you're targeting iOS > 4.3, you can rely on Automatic Reference Counting for memory management. But be sure to understand the base concepts and read the fundamental guide or you'll waste a lot of time programming in the dark.
I ran analyze on my 1st iPhone app, and I see a few potential memory leaks. The app itself works fine on the simulator.
I would like to do the right thing and clear the potential memory leaks, but some are quite puzzling. Maybe someone could help me here?
Thanks in advance.
Pier.
Error 1) The Analyzer says "Potential leak of an object stored in tempDate and tempAns"
#import "Answer.h"
#implementation Answer
#synthesize answerTiming;
#synthesize xPosition;
#synthesize earlyOrLate;
#synthesize hit;
+ (Answer *) createAnswerWithTiming :(NSDate *)paramTiming andXPosition :(float) xPosition
{
NSDate * tempDate = [[NSDate alloc] initWithTimeInterval:0 sinceDate:paramTiming];
Answer * tempAns = [[Answer alloc] init ];
[tempAns setAnswerTiming:tempDate];
[tempDate release];
[tempAns setXPosition:xPosition];
[tempAns setEarlyOrLate:0];
[tempAns setHit:false];
return tempAns;
}
- (void)dealloc {
[answerTiming release];
[self release];
[super dealloc];
}
#end
Error 2) Analyzer says (see below)
- (void)viewDidLoad
{
[super viewDidLoad];
........
...
UIImage * perfectImage = [UIImage imageNamed: #"perfect.png"];
self.perfectImageView2 = [[UIImageView alloc]initWithImage:perfectImage];
// method returns an objective C content with a +1 retain count
[self.perfectImageView2 setFrame:CGRectMake(145.0f,
150.0f,
self.perfectImageView2.image.size.width,
self.perfectImageView2.image.size.height)];
self.view.backgroundColor = [UIColor whiteColor];
UIImage * levelUpImage = [UIImage imageNamed:#"levelup.png"];
self.levelUpImageView = [[UIImageView alloc] initWithImage:levelUpImage];
[self.levelUpImageView setFrame:CGRectMake(100.0f,
400.0f,
self.levelUpImageView.image.size.width,
self.levelUpImageView.image.size.height)];
//object leaked, allocated object is not referenced later in this execution path and has a retain count of +1
self.view.backgroundColor = [UIColor whiteColor];
}
Error 3)
- (NSMutableArray *) generateQuestionTapAnswers:(NSString *) answersString withFirstBeat: (NSDate *) firstBeatTime
{
NSArray * notesToDraw = [answersString componentsSeparatedByCharactersInSet: [NSCharacterSet characterSetWithCharactersInString: #" "]];
float noteValueOffset = 0.0;
NSMutableArray * answerArray = [[NSMutableArray alloc] init ];
// Method returns an objective C object with a +1 retain count
for (int i=1; i < notesToDraw.count; i++) // i = 0 is the time signature
{
.....
}
return answerArray;
// Object returned to caller as an owning reference (single retain count transferred to caller)
// Object leaked: Object allocated and stored into answerArray is returned from a method whose name generateQuestionTapAnswers does not start with copy, mutableCopy
}
Regarding your first and third warnings, the compiler will assume that you'll be creating an object in "a method whose name begins with alloc, new, copy, or mutableCopy" (see Advanced Memory Management Programming Guide). So, if you're returning a +1 object, make sure your method name begins with one of those four prefixes. If you create a +1 object without one of those prefixes, it won't be happy. So, for error #1, if you rename that method to be something like newAnswerWithTiming, that warning should go away. (If the calling method doesn't clean up after them, though, the warning will just be shifted to that routine, but let's take it one step at a time.)
Likewise for error #3, if you rename that method to be something like newAnswerArrayFromQuestionTapAnswers (or whatever ... I'm not sure if I understand your method name ... just make sure it starts with new), you'll similarly eliminate that warning. In both cases, just make sure to release it at the appropriate point because whomever called these two respective methods now "owns" those objects and is responsible for their cleanup.
As an aside, you don't need to do [self release] in your dealloc in your discussion of error #1. (Frankly, I'm surprised it doesn't generate an error.) You don't need it because if self wasn't already at retainCount of zero, you never would have gotten to dealloc in the first place.
Regarding error #2, how are the properties perfectImageView2 and levelUpImageView defined? If retain or strong, you're creating something with a +2 retainCount, and you probably want to add an autorelease after the alloc/init.
I am in the process of optimizing my app and making sure memory management is properly implemented. As I found the didUnload / dealloc / willAppear not reliable for implementing my memory cleanup, I decided to implement my own method so I can have full control of this memory management.
Definition of my arrays in the header file
#property (retain) NSMutableArray *selectedCardIDs;
#property (retain) NSMutableArray *selectedRowArray;
#property (retain) NSMutableArray *cardArray;
#property (retain) NSMutableArray *cardIDArray;
Here the release method:
- (void) willReleaseObjects {
[self.aCopyOfCardIDArray release];
[self.listOfItems release];
[self.aCopyListOfItems release];
[self.selectedCardIDs release];
[self.selectedRowArray release];
[self.cardArray release];
[self.cardIDArray release];
}
The arrays can get very large (> 1'000 entry each), why a release of those arrays is essential after the view is unloaded. I explicitly call this function in the IBAction method, such as:
- (IBAction) stopDictionary:(UIBarButtonItem *) sender {
[self closeDatabase];
[self willReleaseObjects];
[self dismissModalViewControllerAnimated:YES];
}
I debugged the code and it is actually executing each release, however when I diagnose the memory allocation with Instruments, it seems to free up only partial memory, actually almost nothing why the memory consumption of course is increasing every time I load this view again, which is no good at all.
Any idea, why my memory is not released here? Thanks.
Don't call release on a property as you risk something very bad happening in case you have any properties declared as assign or copy. You could potentially release a returned instance which is already autoreleased.
Instead either release the instance variable behind it or set the property to nil. Either way you will achieve the correct result. In case you have any Key/Value observers on your properties the best way is to set the properties to nil which will automatically propagate the values to any observers:
- (void) willReleaseObjects {
self.aCopyOfCardIDArray = nil;
self.listOfItems = nil;
...
}
My app is receiving memory warnings because it's asking for lot of memory. I try to release every allocation. However, sometimes I don't know how to do it.
For example: I have two pairs of .h and .m file. One of them makes connections with a server and the other with local SQLite.
Usually, the code which calls to a method from those files are like this:
-(NSMutableArray *) getRecentActivity{
LocalStorageController *local = [[LocalStorageController alloc]init];
return [local getRecentActivity];
}
getRecentActivity returns a NSMutableArray.
Well, in that piece of code we can see that I am allocating memory for the LocalStorageController but I never call to the release method so, I suppose, the more I call that function, the more memory I will be allocating.
If I call autorelease after init, it will crash.
Moreover, usually, I use this other kind of code:
ServerConnection *serv = [[ServerConnection alloc]init];
NSMutableArray list = [serv getMyListOfContacts];
Which uses ASIHTTPRequest and, if I call [serv release]; after the second line, the app crashes with EXC_BAD_ACCESS pointing to a line in ASIHTTPRequest library.
How is suppose to manage this situation?
Thank you very much!
The first case is easy;
-(NSMutableArray *) getRecentActivity{
LocalStorageController *local = [[LocalStorageController alloc]init];
NSMutableArray *tmp = [local getRecentActivity];
[local release];
return tmp;
}
The second case is hard to solve in a general way without seeing more of the actual code.
Using serv as a property would be fixing this retain/release problem.
In your .h:
#property (nonatomic, retain) ServerConnection *server;
In your .m:
#synthesize server;
- (void)dealloc {
[server release];
// The rest of your releases here...
[super dealloc];
}
- (void)yourMethod {
ServerConnection *myServConnection = [[ServerConnection alloc] init];
self.serv = myServConnection;
[myServConnection release];
NSMutableArray list = [self.serv getMyListOfContacts];
}
Just keep on using self.serv in this class from that point on and you won't have a problem with having the object being released.