iphone - trying to understand #property - iphone

Suppose I have two classes. In the first one I declare this in Class1.h
#interface Class1 : UIViewController {
NSString *myString;
id myObject;
}
On the second class I go beyond that I declare it like
#interface Class2 : UIViewController {
NSString *myString;
id myObject;
}
#property (nonatomic, retain) NSString *myString;
#property (nonatomic, retain) id myObject;
and then I #synthesize myString, myObject on Class2.m
Then, on my main program, I create two objects: one based on Class1 and another one based on Class2.
What effect the #property of class2 will have? Will every value assigned to both values on Class2 be always retained? If so, do I need to "release" them? How?
Thanks.

Please read Declared Properties section of The Objective-C programming language
for a full explanation on properties ;)
In Class2:
In this case you set retain attribute to your property it is supposed to be retained in the implementation. This is done automatically when you synthesize a property.
This means that you should have
- (void) dealloc{
[myString release];
[myObject release];
[super dealloc];
}
and everything should be fine
In Class1, you don't have properties so myString and myObject is not visible from outside. But this does not mean that you shouldn't release them. It depends on the way you initialize them and/or if you send retain messages to them.
BTW, if you set assign a property you don't release it, just set it to nil in the dealloc method. If you set copy to it then you must release it.
EDIT
You said: *But suppose I have this *
#property (nonatomic, retain) UIView *myView;
and
myView = [[UIView alloc] initWithFrame:myFrame];
[self.view addSubview:myView];
[myView release];
? I am already releasing myView... do I have to release it again???
First, since you have your property defined that way, you should have dealloc method as:
- (void) dealloc{
[myView release];
[super dealloc];
}
So, the answer is NO you should not release it but actually is not correct.
Please take a look:
myView = [[UIView alloc] initWithFrame:myFrame]; //myView retainCount is 1
[self.view addSubview:myView]; //retainCount is 2
[myView release]; //retainCount is 1 again
later in dealloc method
- (void) dealloc{
[myView release]; // retainCount becomes 0, is deallocated
[super dealloc]; // subviews of self.view are released but myView was already deallocated!, so you have over released myView once ;(
}
This is the correct way: (Use your properties ;) )
UIView *aView = [[UIView alloc] initWithFrame:myFrame]; // init, retainCount is 1
self.myView = aView; // retainCount becomes 2
[aView release]; // retainCount becomes 1 again and we are fine.
[self.view addSubview:self.myView]; //retainCounts becomes 2 again.
even if it is 2 there is no problem because when self.view is deallocated its subviews also will be released. Hence self.myView retainCount will become 1 again later when self is deallocated.
- (void) dealloc{
[myView release]; //retainCounts becomes 1
[super dealloc]; // all its subviews of self.view are released hence myView retaincount becomes 1 and is released corretly
}
What is the difference?
Suppose self.myView is also retained by other object X and with the former approach, X's view will be pointing to an invalid object, because it was already released.
Hope it helps
EDIT2
As bbum's indication, this is a mini-mini-short tutorial on properties:
when you have
#property (... retain) NSObject *retainVar;
#property (... assign) NSObject *assignVar;
#property (... copy) NSObject *copyVar;
and you #synthesize them
is like having the following setters:
// retain
-(void)setRetainVar:(NSObject *)var {
if (retainVar != var) {
[retainVar release];
retainVar = [var retain];
}
}
//assign
-(void)setAssignVar:(NSObject *)var {
assignVar = var;
}
//copy
-(void)setCopyVar:(NSObject *)var {
if (copyVar != var) {
[copyVar release];
copyVar = [var copy];
}
}
(this means that if you assign directly an object you have to make sure is something equivalent to above setters, from the memory management point of view)
and your dealloc method should be something like:
- (void) dealloc{
[retainVar release];
assignVar = nil;
[copyVar release];
[super dealloc];
}
When setting your ivars
for example, inside of init:
- (id) init{
if ((self = [super init])){
//this is ok
retainVar = [[NSObject alloc] init];//but is retainVar was not nil we will have a leak ;(
//This is better
NSObject *obj = [NSObject alloc] init];
self.retainVar = obj;
[obj release];
//this is BAD
assignVar = [[NSObject alloc] init];//because this is like retaining it, later it will leak
//below is correct
NSObject *obj = [[[NSObject alloc] init] autorelease];
assignVar = obj;
//copy is pretty much like retain,
//this is ok
copyVar = [[NSObject alloc] init]; //but, if copyVar was not nil is a leak!
//below is better
NSObject *obj = [NSObject alloc] init]:
self.retainVar = obj;
[obj release];
}
return self;
}

Apple's "Learning Objective C - A Primer" tells you about that and more:
http://developer.apple.com/library/ios/#referencelibrary/GettingStarted/Learning_Objective-C_A_Primer/

Related

NSMutableArray with memory leak

I am using following code to create NSMutableArray. When I run the same in “Profile” mode, it is showing a memory leakage.
SampleArray.h
#interface SampleArray: NSObject {
}
#property (assign, retain) NSMutableArray *array;
#end
SampleArray.m
#import "SampleArray.h"
#implementation SampleArray
#synthesize array;
-(void) viewDidLoad {
self.array =[ [NSMutableArray alloc] init];
}
-(void) viewWillDisappear:(BOOL)animated {
[self.array release];
}
#end
When I am using autorelease, then I can’t able to access the same in other function or method and return null value. Please help me to find the issue.
releasing this array in viewWilLDisappear is not a good idea, you should release in the dealloc function. You should worry about over-releasing this item and causing a program crash since viewWilLDisappear may get called multiple times during the lifetime of this ViewController.
Anyhow, you are double retaining the item beacuse your property has a retain on it (and make it nonatomic, not assign), add an autorelease to your alloc/init:
self.array =[[[NSMutableArray alloc] init] autorelease];
and move
[array release];
to your dealloc function. Or convert to ARC and don't worry any longer...
Try setting it to (nonatomic, retain), then autoreleasing.
It is better to handle memory de-allocation in your -dealloc() and set your array to nil to be more secure in your -viewDidUnload()
so it will be:
-(void) viewDidUnload
{
self.array = nil;
}
-(void) dealloc
{
[array release];
[super dealloc];
}
and like other people said, declare your property as (nonatomic, retain) instead of (assign, retain)
First of all I'm assuming that you are using
#property (nonatomic, retain) NSMutableArray *array;
use this
-(void) viewDidLoad {
array =[[NSMutableArray alloc] init];
}
-(void) viewWillDisappear:(BOOL)animated {
[array release];
}
I will recommend you to use dealloc instead of viewWillDisappear
-(void) dealloc {
[array release];
[super dealloc];
}
Explanation of your code
-(void) viewDidLoad {
// here you are allocating a mutable array thus retain count becomes one
// then you are assigning it to the property which is retain and thus retains it
// making the retain count 2
self.array =[ [NSMutableArray alloc] init];
}
-(void) viewWillDisappear:(BOOL)animated {
// here you are releasing it so its retain count becomes 1 from 2
// thus shows memory leak
[self.array release];
}

Memory Management example

Here is some of the code from one of my classes, I was wondering am I handling the memory right or am I leaking anywhere?
#implementation CardViewController
#synthesize playerImage;
#synthesize cardLabel;
#synthesize card;
#synthesize frontView;
#synthesize backView;
#synthesize nameLabel;
#synthesize infoLabel;
#synthesize delegate;
-(void) initialiseButtons
{
NSLog(#"initialiseButtons %d",totalButtons);
int ypos = playerImage.frame.origin.y + playerImage.frame.size.height + 42;
for(int i=0; i<totalButtons; i++)
{
StatView *sv = [[StatView alloc] initWithYPos:ypos];
sv.tag = 100 + i;
[sv.overlayButton addTarget:self action:#selector(statTapped:)
forControlEvents:UIControlEventTouchUpInside];
sv.overlayButton.tag = 10 + i;
[self.frontView addSubview:sv];
ypos += 26;
}
}
-(IBAction) statTapped:(id) sender
{
UIButton *tappedButton = (UIButton *)sender;
int tag = tappedButton.tag;
if(delegate && [delegate respondsToSelector:#selector(cardTapped:)]) {
[delegate cardTapped:tag-10];
}
}
-(void) viewDidLoad
{
NSLog(#" viewDidLoad CVC");
[self initialiseButtons];
metaData = [[NSArray alloc]
initWithObjects:#"Height",#"Weight",#"Games",#"Attack",#"Defense", nil];
}
-(void) setCard:(Card *)newCard
{
NSLog(#"setCard");
[card release];
card = [newCard retain];
[self updateUI];
}
- (void)dealloc
{
[card autorelease];
[metaData autorelease];
[super dealloc];
}
#end
Where should I release StatView *sv = [[StatView alloc] initWithYPos:ypos];
If i released it every loop wouldnt that cause problems?
Besides that do I handle the rest of the memory ok?
Thanks
-Code
Yes, you should release that StatView at the end of each loop iteration, when you've inserted it into the view hierarchy.
You should try the analyzer built into XCode as it is very good at finding these type of memory leaks. Have a look.
Release the new StatView after this line
[self.frontView addSubview:sv];
[sv release]; // frontView retains sv
Release all properties declared as retain or copy in dealloc. Candidate properties: playerImage, cardLabel etc. Send a release message, not autorelease
\\[card autorelease];
[card release];
In viewDidUnload release all properties that are declared as IBOutlet and set the variable to nil
[frontView release], frontView = nil;
You should release it as soon as you've added it to the view hierarchy (by sending addSubview: with the newly-allocated view as an argument). This is because UIView objects retain their subviews.
One other problem I notice: your setCard method should first check whether the new and old cards are identical, and do nothing in that case. Otherwise, you may release your existing card, then try to retain it again, only to find that it has been dealloced.

Unused IBOutlets leaking

So, I'm loading by XIB file and it contains a set of UIBarButtonItems. Some of the items are used when the viewDidLoad: is called.
#interface MyViewController : UIViewController {
IBOutlet UIBarButtonItem *addButton;
IBOutlet UIBarButtonItem *editButton;
IBOutlet UIBarButtonItem *doneButton;
}
// NB: There are no properties retaining anything.
#end
#implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *initialToolbarItems =
[[NSArray alloc] initWithObjects: addButton, editButton, nil];
self.toolbarItems = initialToolbarItems;
[initialToolbarItems release];
}
- (void)dealloc {
[super dealloc];
// Nothing else to do here since we are not retaining anything.
// … or are we? <insert dramatic music here>
}
#end
If I push the above the above ViewController onto a UINavigationController everything seems fine, all the IBOutlets are assigned and behave like expected.
The instant i pop the ViewController from the navigation stack Instruments' Leaks tells me that I am leaking a UIBarButtonItem. Woe is me!
If I change dealloc: to
- (void)dealloc {
[doneButton release];
[super dealloc];
}
no leaks occur. The same goes if I use doneButton in viewDidLoad:
NSArray *initialToolbarItems =
[[NSArray alloc] initWithObjects: addButton, editButton, doneButton, nil];
My question: Why is my IBOutlet leaking when I don't use it. I don't retain it at any point. The the NIB loader should own the object, right?
Only thing I can think of:
The nib loader treats IBOutlets as strong references. All outlets are retained by default unless you specifically indicate assign. So you still need to release them in dealloc and viewDidUnload.
You can also use a assigned property to make it a weak reference:
#property (nonatomic, assign) IBOutlet UIBarButtonItem *doneButton;
Some reading: http://weblog.bignerdranch.com/?p=95
If you have #property with (retain) declared for the your IBOOutlets they will be retained and must be released
The array retains them

iPhone SDK: How/when should I release a UITableView delegate object?

I am using a custom class as the delegate and datasource on a UITableView. I'm doing (something like) this in my viewDidLoad method:
MyClass *myObject = [[MyClass alloc] init];
tableViewOutlet.delegate = myObject;
tableViewOutlet.dataSource = myObject;
Surely I need to decrease the retain count on myObject somewhere? But calling [myObject release] here has very bad results - the delegate gets destroyed before the table has finished doing its stuff.
I have tried
MyClass *myObject = [[[MyClass alloc] init] autorelease];
but it also has terrible consequences.
Do I have a memory leak here? If so, how and when do I release the delegate safely?
Your interface file:
#interface SomeClass: NSObject {
MyClass *myObject;
}
#property (nonatomic,retain) MyClass *myObject;
#end
Your implementation file:
#implementation SomeClass
#synthesize myObject;
-(void)dealloc {
// if you want to be safe, change tableViewOutlet properties...
// tableViewOutlet.delegate = nil;
// tableViewOutlet.dataSource = nil;
[myObject release]; // retain = 0
[super dealloc];
}
...
MyClass *obj = [[MyClass alloc] init]; // retain = 1
self.myObject = obj; // retain = 2
// NOTE: if you instead write: myObject = obj; **NO** retain msg will be sent.
// *not* what you want in this context.
tableViewOutlet.delegate = obj; // assign, so retain =2
tableViewOutlet.dataSource = obj; // assign, so retain = 2
[obj release]; // retain = 1
...
#end
make myObject an instance variable by declaring it in the #interface (.h file), then call...
[myObject release];
.. in your dealloc method.

Why is this line of Objective-C leaking memory?

I'm writing an iPhone app. I have a header file that looks like this:
#interface EditTagsViewController : UITableViewController {
NSMutableArray *allTags;
NSMutableArray *selectedTags;
NSInteger currentFavorite;
}
#property (nonatomic, retain) NSMutableArray *allTags;
#property (nonatomic, retain) NSMutableArray *selectedTags;
#property (nonatomic) NSInteger currentFavorite;
#end
In the implementation file, my viewDidLoad method looks like this:
- (void)viewDidLoad {
NSMutableArray *aTags = [[NSMutableArray alloc] initWithArray:[Tag findAllTags]];
self.allTags = aTags;
[aTags release];
NSMutableArray *sTags = [[NSMutableArray alloc] initWithArray:[Tag findByFavoriteId:currentFavorite]];
self.selectedTags = sTags;
[sTags release];
UIBarButtonItem *add = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(addNewTag:)];
self.navigationItem.rightBarButtonItem = add;
[add release];
[super viewDidLoad];
}
Here is my dealloc method:
- (void)dealloc {
[allTags release];
[selectedTags release];
[super dealloc];
}
What's confusing to me is that when I run the app both in the simulator and on the device itself, using Instruments (memory leaks), it tells me that this line in my viewDidLoad method is leaking an array:
self.selectedTags = sTags;
It's confusing because I'm using the exact same technique with 2 different variables, and yet no leak is reported with the first one.
I feel like I'm missing something obvious here. Any ideas?
Your code looks correct to me. Is it possible that one of [Tag findAllTags] or [Tag findByFavoriteId:] is leaking? Are you making sure to set self.allTags and self.selectedTags to nil in dealloc?
Be mindful of the difference between saying self.allTags = ... and allTags = .... Because allTags is a property and has the retain attribute, whenever you assign via self.allTags = ..., it implicitly calls the setter method [self setAllTags:...], which invokes retain on the new value and release on the old value (if any). You're doing it correctly in this code sample, but if elsewhere you're assigning straight to allTags (without the self.), you're not releaseing the old value, which may be the source of the leak. Likewise for selectedTags.
Have a look at findByFavoriteId is there a retain there? That is the only difference I can see between the aTags and sTags are used in your example