Subview of UIButton needs releasing? - objective-c

First time asking a question here.
I have a question, I have a UIImageView added as a subview to my UIButton, which is declared using buttonWithType: (which means I don't have to release the button right?) But do I still have to release the subview of my UIButton?
Code bits:
UIImage *circleImage = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource: #"item-circle" ofType: #"png"]];
UIImageView *circleImageView = [[[UIImageView alloc] initWithImage: circleImage] autorelease];
[imageView setFrame: CGRectMake(-5, -5, 65, 65)];
UIButton *button = [UIButton buttonWithType: UIButtonTypeCustom];
[button addSubview: circleImageView];

Short Answer:
Your code seems fine. The basic rule of thumb is that for every alloc, new, retain, or copy, you need a release or autorelease.
Long Answer:
Let's go through your code line by line.
UIImage *circleImage = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource: #"item-circle" ofType: #"png"]];
This first line uses a convenience method. you don't need to call release on anything, since you haven't called alloc, new, retain, or copy.
UIImageView *circleImageView = [[[UIImageView alloc] initWithImage: circleImage] autorelease];
In the second line, you call alloc, but you then call autoerelease, so you're good there.
[imageView setFrame: CGRectMake(-5, -5, 65, 65)];
Again, no alloc, new, retain, or copy.
UIButton *button = [UIButton buttonWithType: UIButtonTypeCustom];
Once again, you've used a convenience method.
[button addSubview: circleImageView];
You still haven't called alloc, new, retain, or copy. Therefore, you don't call release or autorelease.

As a general rule, anything you alloc or retain yourself will need to be released. But you are doing that here (in essence) by calling autorelease. If you're asking whether or not you need to release the subview again, the answer is no.
The same applies to your button. You haven't called alloc or retain (instead you used buttonWithType), so you don't need to call release on it.

Related

ARC and ViewControllers

I have a little misunderstanding regarding ARC. I'm creating a new UIViewController using the following code:
CGRect screenRect = [[UIScreen mainScreen] bounds];
LocationProfileView *locationProfile = [[LocationProfileView alloc] initWithLocation:l];
locationProfile.view.frame = CGRectMake(0, screenRect.size.height, screenRect.size.width, 400);
[appDelegate.window addSubview:locationProfile.view];
[UIView animateWithDuration:.25 animations:^{
locationProfile.view.frame = CGRectMake(0, 0, screenRect.size.width, screenRect.size.height);
}];
In its UIVIew I put a button which removes the view from screen. The problem with this is that locationProfile gets deallocated immediately after its being added to screen, so everytime I'm trying to tap on "Close" button (which calls a method in LocationProfileView class) my application will crash.
So I added a property:
#property(nonatomic, strong) LocationProfileView *locationProfile;
and changed the second line of code in:
locationProfile = [[LocationProfileView alloc] initWithLocation:l];
but now my class won't be deallocated until I initiate it again (because it loses the reference to the first instance of LocationProfileView?). What should I do to make my class being deallocated every time I tap on "Close" button? I guess that setting locationProfile to nil would work, but this means that I'll have to call a method in the main class (the one which contains the code block).
What is the proper way for doing this? Sorry if my questions is too noobish.
Note:
l is an instance of a custom class which contains some infos to be displayed in LocationProfileView's UIVIew.
- (void)closeButtonCallBack {
[self.locationProfile removeFromSuperview];
self.locationProfile = nil;
}
i am assuming the target of your close button is the viewcontroller itself
a strong pointer will the retain the object until the viewController itself is deallocated, unless you assign to it nil
a local variable will be deallocated when it goes out of scope
ALTERNATIVELY
without using the strong pointer, you can do this
LocationProfileView *locationProfile = [[LocationProfileView alloc] initWithLocation:l];
UIButton *close = [UIButton buttonWithType:UIButtonTypeRoundedRect];
close.frame = CGRectMake(0, 100, 100, 30);
[close addTarget:locationProfile action:#selector(removeFromSuperview) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:close];
In your original example,
LocationProfile *locationProfile=...
is a local variable. So it's released as soon as you return from the constructor. That's what you observed.
When you make it a strong property, the view controller retains the locationProfile:
#property(nonatomic, strong) LocationProfileView *locationProfile;

How to release an allocated bar button item

Can I autorelease the following?
self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithCustomView:backButton] autorelease];
it works and removes an Analyzer warning I was getting. if not, how would I go about releasing it correctly.
thanks for any help
This is correct.
The leftBarButtonItem is a property that retains the UIBarButtonItem, so yes it is a good approach.
Without the autorelease message it will cause a memory leak.
it is correct, also you can do this
UIBarButtonItem *item = [[[UIBarButtonItem alloc] initWithCustomView:backButton];
self.navigationItem.leftBarButtonItem = item;
[item release];
That's the right way (although the right way now is to use ARC).
An accepted alternative (in non-ARC environment) is:
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:backButton];
self.navigationItem.leftBarButtonItem = barButton;
[barButton release];
The setter of navigationItem retains the button itself so the object returned by alloc/init has to be released in order to balance the retain counts in one of the
described ways (alloc/init returns object with retaincount 1 and the setter adds +1 to that).
This is definitely NOT recommended:
[self.navigationItem.leftBarButtonItem release];

Do I need to remove views from superviews in dealloc?

If I alloc/init a view and add it to a another view in code (I did not use a xib) - do I need to remove it when the containing UIViewController's dealloc message is sent? I have seen this code in certain places, and wondered is it necessary under some circumstances to free memory?
Thanks,
Marc
If you do this,
UIView *v = [[UIView alloc] init];
[self.view addSubview:v];
[v release];
or
UIView *v = [[[UIView alloc] init] autorelease];
[self.view addSubview:v];
,the v will be released when its parent view release;
When the parent view use addSubview, it will retain the subview, and will release the subview when it is released.
This isn't necessary. All UIView subclasses hold subviews array, which gets released in the final UIView dealloc message, which releases your views.

Objective-C memory management: What is happening when setting a retain property?

I'd like to understand the memory management implications of the following line of code:
// in tableView:cellForRowAtIndexPath
cell.accessoryView = [[UIImageView alloc] initWithImage:
[UIImage imageNamed:#"test.png"];
I'm calling alloc which usually means I call release somewhere. The UITableViewCell's accessoryView setter property is retain so (I think) the cell will "take ownership" of the of the UIImageView. What exactly is happening in the above line of code in regards to memory management?
If you don't release the view somewhere then it will be leaked. So you might want to do
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"test.png"];
cell.accessoryView = imageView;
[imageView release];
or you could also do
cell.accessoryView = [[[UIImageView alloc] initWithImage:
[UIImage imageNamed:#"test.png"] autorelease];
First: +alloc retains the UIImageView (or, alternately, "you start with ownership of the UIImageView")
Second: +imageNamed autoreleases the UIImage (or, "+imageNamed does not give you ownership of the UIImage")
Third: the setter for accessoryView retains the UIImageView (or, "the accessory view takes ownership of the UIImageView")
Since you now have two owners for the UIImageView, that's probably a leak unless you're intentionally keeping it around to use later, and managing it accordingly.
Think of it this way: you're calling alloc/init, so you own it. You must release it when you no longer want to own it.
You can assume that cell.accessoryView takes ownership unless the docs say otherwise (like with delegates), so once you assign it to cell.accessoryView, you probably don't need to own it anymore. You should release it.
In summary, that line is retaining at least twice: once with the alloc/init and at least once with the assignment to cell.accessoryView. You are only responsible for one release, the one for alloc/init.

ObjC : difference between self.obj and obj

What is the difference between self.obj and obj in objective C?
I'm asking this because when I write [view method] it's fine
but when trying [self.view method] it's an infinite loop
In fact the code is the following:
//---create a UIView object---
UIView *view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
view.backgroundColor = [UIColor lightGrayColor];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[view addSubview:button];
self.view = view;
The problem is that [self.view addSubview:button] gives an infinite loop.
Using obj is just what it looks like: direct variable access.
However, self.obj is not what it looks like. Instead, it is context-dependent syntactic sugar for a call to an accessor method. By convention, the setter method would be setObj: and the getter obj, though it is possible to override this. So typically self.obj is equivalent to either [self obj] or [self setObj:x] depending on whether you're reading from the value or assigning to it.
In your example, when you put [self.view method], what you are really doing is [[self view] method]. Without knowing more about the context in which you're using this, it's hard to say why it's giving you an infinite loop, although one obvious case that would do that is if you were making this call inside the accessor method for view.
When you're using '.' you're accessing property. And when you're typing [view method] you're accessing the variable named view. See my answer for this question.
for example:
-(void) doSmth {
UIView* view = [[UIView alloc] init];
[view method]; //sends message 'method' to view variable declared in this function
[self.view method]; //sends message to instance variable of self (accessing it via generated accessor)
}