Is this proper memory management? What I'm wondering is if I am supposed to release after the alloc in -viewDidLoad.
SomeViewController.h
#import <UIKit/UIKit.h>
#interface SomeViewController : UIViewController {
UIButton *someButton;
NSString *someString;
}
#property (nonatomic, retain) IBOutlet UIButton *someButton;
#property (nonatomic, copy) NSString *someString;
#end
SomeViewController.m
#import "SomeViewController.h"
#implementation SomeViewController
#synthesize someButton, someString;
- (void)viewDidLoad {
[super viewDidLoad];
someButton = [[UIButton alloc] init];
someString = [[NSString alloc] init];
}
- (void)viewDidUnload {
self.someButton = nil;
self.someString = nil;
}
- (void)dealloc {
[someButton release], self.someButton = nil;
[someString release], self.someString = nil;
[super dealloc];
}
#end
Thanks
Edit: one more thing. If I place a UIButton in IB, do I still need to alloc it?
It's quite a long story which you may find by googling around, and someone may enter here, but to keep it short, here's a few tweaks:
- (void)viewDidLoad {
[super viewDidLoad];
self.someButton = [[[UIButton alloc] init] autorelease];
self.someString = [[[NSString alloc] init] autorelease];
}
I.e. use the setters (self.something = ...;) and always either release or autorelease any alloc you do. (The logical distinction would be who "owns" the objects; with these tweaks the function gives up ownership and the class gets it.)
edit: no, if you create a button in IB, the button will just be there, allocated and initialized with your styles.
When you call init on an object, the retain count goes to 1. You have two different setter attributes: one is "retain" for your UIButton, and the other is "copy" for your NSString. When you call
self.someButton = someUIButtonObject;
someUIButtonObject gets a retain message and so its retain count goes up to 1. In the case of your original code, calling release in dealloc will release one reference to someUIButtonObject, but it will still have a retain count of 1 and will thus be leaked.
The other case with your NSString has a different problem but still leaks memory. Here your call to [[NSString alloc] init] results in a new string object, and then calling
self.someString = someNSStringObject;
results in the creation of a brand new object which copies the content of someNSStringObject. In this case, someNSStringObject and the setter's copied object each have a retain count of one. Here, you leak the string you alloc init-ed because you no longer have a reference to it and it goes out of scope with a retain count of one.
A quick side note: I don't know what your actual code looks like, but don't forget that NSStrings are immutable (so just calling [[NSString alloc] init] is pretty useless) and UIButton needs a frame (try [[UIButton alloc] initWithFrame:(CGRect)frame];).
Basically you need to match each call to retain, copy, or alloc with a call to release (or autorelease). It is appropriate so use
self.someButton = [[[UIButton alloc] init] autorelease];
Which will release the object, though at some unknown time in the future. Don't use autorelease if your memory is very tight and you need memory ASAP. In that case you would do:
UIButton* tempButton = [[UIButton alloc] init];
self.someButton = tempButton;
[tempButton release];
which guarantees that you don't have large objects waiting around in your autorelease pool.
Also, always use the getters/setters to access these properties (self.someButton as opposed to someButton). That way you don't accidentally assign someButton to a new pointer and leak the old one.
When you create a button in IB, the XIB holds a reference to the button (+1 retain count). If you create an IBOutlet so you can access the button programmatically, you get another retain on the object, so if you have an IBOutlet to someButton, someButton has a retain count of 2 as soon as the XIB is loaded. You do not need to alloc the object, that is done automatically when the XIB is loaded into memory. Also, you are only responsible for releasing one reference to the button (your IBOutlet reference). In general, it is a good practice to release your reference as soon as you know you no longer need it. For example, if you know you need a button and have to do initial customization and nothing else, then you would probably do something like this:
-(void)viewDidLoad {
// Do some customization of someButton
[someButton release];
}
Otherwise you would probably release someButton in viewDidUnload since you know at that point you won't need the reference to the button anymore.
If your button is set in IB, you do not need to allocate it in the code (IB does it for you).
Basically, each object you created using alloc or new, should have a release somewhere in your code. If it's an interface variable, you should release them in the dealloc function.
Related
#interface Foo : NSObject
#property (nonatomic, retain) Bar * bar;
#end
#implementation Foo
#synthesize bar = _bar;
- init {
self = [super init];
if (self) {
_bar = [[Bar alloc] init];
// Or
_bar = [[[Bar alloc] init] autorelease];
}
return self;
}
- (void)dealloc {
[_bar release];
[super dealloc];
}
#end
When I run the analyzer, both
_bar = [[Bar alloc] init];
and
_bar = [[[Bar alloc] init] autorelease];
are fine.
Which one I should use?
You should use the first. It creates a retained object, whereas the second "autoreleases" that retain.
The important consideration is that you're assigning it to instance variable _bar. If you were, instead, assigning it to property self.bar, then the retain directive in the property declaration would cause the object to be retained, so assigning an autoreleased value would be appropriate. But since you're assigning to the "bare" instance variable instead, you need to handle the retain yourself, so you need the first form.
PS: I'm a bit surprised that the analyzer doesn't complain about the second version.
PPS: It should be noted that the choice here is highly context dependent. But you included enough context (the property definition) to make the choice. Without seeing the property definition (or other info, in the case of a non-property) it would be hard to say.
The autorelease version is not correct and may cause crashes in some situations—your first line results in _bar having a retain count of 1, and thus sticking around until it's released in -dealloc when you no longer need it.
The second line, however, releases the object soon-ish (specifically, at the end of the run loop), and thus could cause it to disappear when you still need it.
Read Apple's Guide on Memory Management for more information.
I'm having issues with data persisting inside of multiple instances of objects I'm creating.
I have a class "IconViewController" that extends UIViewController that I pass information to, such as the name of the image it should be using:
//IconViewController.h
#interface AppIconViewController : UIViewController
{
NSString *imageName;
}
#property (nonatomic, retain) NSString *imageName;
- (void) doSomething;
//IconViewController.m
#implementation AppIconViewController
#synthesize imageName;
NSNumber *iconWidth;
- (void)loadView
{
[super loadView];
UIImageView *iconImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imageName]];
iconWidth = [NSNumber numberWithFloat:iconImage.bounds.size.width];
[iconImage release];
NSLog(#"iconWidth: %f", [iconWidth floatValue]);
}
- (void) doSomething
{
NSLog(#"iconWidth: %f", [iconWidth floatValue]);
}
In another view controller, I'm instantiating several instances of these IconViewControllers and passing different sized images to them:
AppIconViewController *appIcon1 = [[AppIconViewController alloc] initWithNibName:nil bundle:nil];
appIcon1.imageName = #"Image65PXWide.png";
[self.view addSubview:appIcon1.view];
AppIconViewController *appIcon2 = [[AppIconViewController alloc] initWithNibName:nil bundle:nil];
appIcon2.imageName = #"Image105PXWide.png";
[self.view addSubview:appIcon2.view];
Okay, the weirdness is that when I'm creating these, I'm getting logs back that are accurate...appIcon1 logs "iconWidth: 65.0" and appIcon2 logs "iconWidth: 105.0". But when I call:
[appIcon1 doSomething];
...my log is "iconWidth:105.0".
Why is the data in the first instance reflecting the data in the second instance? What am I missing?
EDIT:
I know that if I declare iconWidth in the header and synthesize it as a property, that it will work. So what I'm wondering is how to make a private version of it persist. Because I tried retaining the NSNumber with:
iconWidth = [[NSNumber numberWithFloat:iconImage.bounds.size.width] retain];
...and it still doesn't work. Does it have to be synthesized and public?
EDIT #2:
Okay, so I figured out that once I declare iconWidth in my header, it works just fine, and I don't have to synthesize it so it keeps it private. Not sure why exactly it won't work if declared in the implementation file - does anyone have any insight into why and if there's any purpose in declaring variables at the top of an implementation but not in the header? Just curious now more than anything.
Synthesizing doesn't make anything private. It just generates getter/setter methods according to declared properties.
By placing iconWidth in the implementation, outside of any methods, it's essentially a class-level variable. So it gets overwritten by the last thing that writes to it. You already have imageName declared in the interface, so why not just put iconWidth there as well (instance variables are private by default)?
In your method doSomething, you are assuming iconWidth is set up properly.
However, iconWidth is created as an autorelease object in loadView method, so when loadView finishes, the main loop will release iconWidth and you are getting random values.
To fix this you have to retain iconWidth so you can use it in other methods
iconWidth = [[NSNumber numberWithFloat:iconImage.bounds.size.width] retain];
As a general rule, method that doesn't start with init will return autorelease object, so you have to be careful how you instantiate an object and whether you need to call retain on it.
Sorry about the title being extremely vague, I'm new to Objective C and struggling a little with it. Basically I have the following section of code:
Graph *graph1 = [[Graph alloc] init];
[graph1 addNode:#"TEST"];
which is working to a degree. But I want to change it because the above code happens on a button press, and therefore I assume I am creating a new "*graph1" every time I do this. I thought I could simply change it to this:
if(self = [super init])
{
[self setGraph: [[Graph alloc] init]];
}
return self;
Where the above is in the init method, and below is the modified function:
[graph addNode:#"TEST"];
However when debugging I've found addNode method is never called when it's like this.
Thanks
Zac
This is testViewController.h
#import <UIKit/UIKit.h>
#class Graph;
#class Node;
#interface testViewController : UIViewController {
Graph *graph;
UILabel *label;
}
#property (nonatomic, retain) IBOutlet UILabel *label;
#property (nonatomic, retain) Graph *graph;
- (IBAction) buttonPressed:(id)sender;
#end
This is textViewController.m
#import "testViewController.h"
#import "Graph.h"
#implementation testViewController
#synthesize label, graph;
- (id)init
{
if(self = [super init])
{
[self setGraph: [[Graph alloc] init]];
}
return self;
}
- (IBAction)buttonPressed:(id)sender
{
//Graph *graph1 = [[Graph alloc] init];
[graph addNode:#"TEST"];
Node *node1 = [[Node alloc] initWithLabel: #"LABEL"];
label.text = node1.label;
}
The first thing that comes to mind is that graph is nil and thus invoking a method (sending a message) to it will result in nothing. An unwanted release will cause an EXC_BAD_ACCESS, and this seems to be not the case.
I suppose you are calling all of this in a UIViewController subclass, right? Are you sure the right init is called? If you are using a NIB you should override the -(id)initWithNibName:bundle: and place you code there. I guess the code is probably in the plain -(id)init, since you are calling [super init] and not [super initWithNibName:nameOrNil bundle:bundleOrNil], but this way, if initialize the controller with the NIB you custom code is never called and thus graph is nil.
By the way, if the graph property is (retain) you are also causing a memory leak in the init.
I'm sure you're through this problem now, but I agree that the reason "add" is "not being called" is that your graph object is nil at that moment
I'd say, first put a test message around your "addNode" call
NSLog(#"We tried to add here");
[graph addNode:#"TEST"];
That will show you that the add is being called -- I bet it is.
Then, where you had your previous call to initialize "graph" right before your add call, try conditionally initializing it:
if(graph == nil) graph = [[Graph alloc] init];
[graph addNode:#"TEST"];
Note, this is all just to find the problem. Finally I'd say you have some challenges in here with how you are dealing with memory. You may not have reference issues, but later leaks. And depending upon how often this code is executed it could be an issue.
But at least you may get to your issue easier with the above tests.
Have you declared the graph variable in the header? ie: Graph *graph; and the corresponding #property (nonatomic, retain) Graph *graph;
Then I would do this:
-(id) init {
graph = [[Graph alloc] init];
[graph retain];
}
that might help (the only reason I think it wouldn't would be because a) if your variable wasn't declared then you would get a warning like graph may not respond to addNode and if it wasn't retained then your app would crash when it runs)... other than that, I can't see what would be the problem. If that doesn't work, can you please post all your code from your .h and .m files?
Then I would do this:
-(id) init {
graph = [[Graph alloc] init];
[graph retain];
}
that might help
This would result in a memory leak. The retain count of the object pointed to by graph will have a retain count of 2. Not ideal. If you declare the property with the retain attribute then
[self setGraph:[[[Graph alloc] init] autorelease]];
should do it. I go with -
self.graph = [[[Graph alloc] init] autorelease];
There could be many reasons the addNode: method is not being called. Put break points in the enclosing method and see if everything is working as you expect it to.
I'm getting this error when trying to see the contents of a NSMutableArray:
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00000021
0x94d5a688 in objc_msgSend ()
ViewController.h:
#interface PeopleViewController : UITableViewController {
NSMutableArray *people;
}
#property (nonatomic, retain) NSMutableArray *people;
ViewController.m:
#implementation PeopleViewController
#synthesize people;
In viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
// initialize our people array with an autoreleased object
people = [NSMutableArray array];
... Populate the people array with Person objects.
}
When I'm at the point where I'm modifying the contents of a cell in the tableview, I'm unable to access the people array in gdb when typing 'po self.people':
Person *person = [[Person alloc] init];
person = [self.people objectAtIndex: indexPath.row]; // <--- 'po self.people' called
cell.textLabel.text = person.personName;
Any ideas why I can't access it?
The line
people = [NSMutableArray array];
returns an autoreleased array that will be released on the next iteration of the current run loop. You should retain that:
people = [[NSMutableArray array] retain];
and of course release it in your dealloc method.
However: Apple engineers have often mentioned in conferences to avoid autoreleased instances like this whenever possible in the iPhone, for performance reasons. Try using alloc/init instead:
people = [[NSMutableArray alloc] initWithCapacity:1];
with the corresponding release in the dealloc method. In this case you don't even need to retain (init returns an instance with a retain count of 1, which is what you need).
And justin's comment is correct: you should do this instead:
Person *person = [people objectAtIndex:indexPath.row];
cell.textLabel.text = person.personName;
and this should work.
is indexPath.row > [people count]?
Also, why are you doing this:
Person *person = [[Person alloc] init]
You're allocating memory, and then pointing to completely different memory.
You can avoid having to fuss with retaining properties by using the self notation to call the accessor and setter methods created by the #synthesize directive.
When you set the people property directly in viewDidLoad it sets the property but does nothing for memory management. However, if you set it with self.people you actually call the synthesized setter method that because of the retain setting of the #property directive will automatically retain the assigned array.
As an aside, I would recommend always using -[NSMutableArray initWithCapacity:] instead of a bare init. It is the actual initializer for the class. You can call it with just '1' if you don't know how big it will be. In the past, I have seen odd problem arise from just using bare init.
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