Arc: Setting references to nil, for multiple buttons - iphone

When displaying an item such as a button in a ViewController, I declare it in the header file:
#property (nonatomic, strong) UIButton *startButton;
Synthesize it in the implementation file:
#synthesize startButton;
Initiate it in the implementation file:
startButton = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
And set the reference to nil in viewDidUnload:
[self setStartButton:nil];
However, what if I have 60 listed items (in a UIScrollView) which are created dynamically in a loop, in viewDidLoad (fed from Core Data)?
(Not using TableView, because the items are quite complicated and interactive.)
Should I declare the items anywhere first? Should I set them to nil anywhere? (Or should I just keep my fingers crossed and pray to the Arc?)
Some guidance would be appreciated. Thanks.

Does your view controller have properties or instance variables that point to each of those 60 items?
Yes: Then set those ivars or properties to nil in your -viewDidUnload.
No: Then there's nothing to worry about.
ARC changes what you do to manage memory, but it doesn't change the basic philosophy, which is that every object should take care of its own references to other objects but not worry about anybody else's.
In this case, the view that contains all those items will have strong references to the items (via it's array of subviews). When the view controller releases its view, that view will then be deallocated and release all its subviews in the process. So all those items will be taken care of. If you did have strong references to any of those items, those references will prevent the referenced objects from being deallocated -- setting the references to nil will avoid that.

If you are creating these views on the fly and adding them as subviews of the scrollView, then you don't need to do anything else.
When the scrollView is released during -viewDidUnload, it will properly clean up all its subviews.
Update
Think of it this way. In a normal view setup on a storyboard, you don't need have a property for every view, only the views your controller interacts with. This is basically the same thing. You have a bunch of views that are embedded into a superview. If the controller doesn't interact with them, then you don't need to worry about them.

no. :)
you definitely set the all pointer to nil what are not necessary anymore when you are using the ARC.
the ARC will release the memory of the current object when there is no more strong pointer who tries to keep alive the object. if you don't set them to nil the ARC thinks you are still using them and they will be in the memory and they will cause serious memory leak.
UPDATE #1
if you have any reference for the buttons this is the easiest way to do it, you don't need to do set to nil anything, because their superview class retain them and you should just remove from their superview.
for (UIView *_subView in self.view.subviews) {
if ([_subView isKindOfClass:[UIButton class]]) {
[_subView removeFromSuperview];
}
}
if you're storing their pointer any other place (in an NSArray for ex.) you must have to remove the pointers from them as well.

Related

Weak and strong properties in -viewDidUnload under ARC

I am new to iphone development . I am using ARC for my project. As far as I understood using ARC we don't have to release any object manually. But , I have observed in some places , people explicitly set their object to nil in the ViewDidUnload even after using ARC.
For example, in .h file I have something like this:
#property (unsafe_unretained, nonatomic) IBOutlet MKMapView *mapViewOutlet;
#property (unsafe_unretained, nonatomic) IBOutlet UIToolbar *toolBar;
#property (strong,nonatomic) NSMutableArray *dataArray;
And .m as follows:
- (void)viewDidUnload
{
[self setMapViewOutlet:nil];
[self setToolBar:nil];
[super viewDidUnload];
self.dataArray=nil;
}
My question is, is it really necessary to explicitly specify nil in the ViewDidUnload even under ARC?
The whole point of the viewDidUnload method is to release data that you don’t really need, in order to free memory. Read the documentation:
When a low-memory condition occurs and the current view controller’s
views are not needed, the system may opt to remove those views from
memory. This method is called after the view controller’s view has
been released and is your chance to perform any final cleanup. If your
view controller stores separate references to the view or its
subviews, you should use this method to release those references. You
can also use this method to remove references to any objects that you
created to support the view but that are no longer needed now that the
view is gone. You should not use this method to release user data or
any other information that cannot be easily recreated.
So you’re setting the properties to nil in order to release the objects now and help the system to free up some memory. But of course this depends on the property type – strong properties are “yours” and only you can decide whether to release them now (by setting to nil) or not. Weak properties could already be nil, for example if they pointed to some views that got released with the main view. And unsafe_unretained properties are a special beast. The object they point to might already been released, but that does not mean they were set to nil automatically. So you should either use one of the “safer” property types (strong/weak), or set the unsafe properties to nil here, to make sure you won’t use the released object later. There are no hard rules in this case, you have to think about the situation and what it means for the various properties.
By the way, viewDidUnload is getting deprecated in iOS 6, where no views are being released under low-memory conditions anymore. You still receive the didReceiveMemoryWarning callback, so that you can release some resources there if you want to. Again, I suggest that you read the documentation and run a few tests to see what happens and decide what you should do.
ARC will only release properties which do not hold a strong reference to an object. In your case, these are all strong references, so they will be kept unless they are explicitly set to nil.
The viewDidUnload method does not mean that your UIViewController is removed from memory, it simply means that its views are removed from memory (iOS Developer - ViewController lifecycle).
In this case, your UIViewController remains in memory, and therefore its properties as well, unless they are explicitly set to nil.
When you are using unsafe_unretained, you should assign it to nil because it will not be assigned to nil implicitly, where is case of weak reference it will be assigned to nil implicitly.So in order to avoid any dangling reference you need to assign to nil in case of unsafe_unretained.

UIViewControllers... Memory Management With SubViews

I have UIViewController who has several subviews but tracking each and every subview allocated is difficult task as the actually coder is not me and need to handle the memory consumption.
My question is there a way to control the memory calling a recursive function to remove and release all the subViews in the UIViewController without knowing the actual reference name ?
As in the code below :
for (UIView* subview in view){
[subview removeFromSuperView];
[subview release] ;
subview = nil;
}
When you call removeFromSuperview on a view it will automatically decrement the retain count (because the superview no longer requires a reference to the view you've just removed).
If you have added your views to the superview in a standard manner there shouldn't be any need to do what you're doing - either you've added your views and then released them, or your views are properties and the release happens later.
The code you're proposing (the recursive loop on all subviews) is a bad idea, because you don't actually know whether your subview is safe to be released or not. You could easily trigger a bad access.

Memory management when using IBOutlet/NIBs

My app is nearly finished and I have been Profiling it using Instruments. I'm checking out retain counts of various objects.
I have been careful to release any objects which I have called alloc on, and these don't seem to be leaking - so thats cool.
However, I have a view controller which has a UIPickerView in it. I set that up by dragging it onto my NIB in IB, defined the property using IBOutlet, synthesized it, and then hooked it all up.
Every time I launch the view, it seems the number of UIPickerViews increases by one. I was under the assumption that I do not need to release this kind of thing myself, as I had assigned it to a property (using nonatomic, retain).
This is happening to all my UI stuff - buttons etc, not just the picker view. I was just using that as an example.
Can anyone help me out here?
Thanks!
When the view is loaded all the items created from the NIB are retained for you and your IBOutlet pointers are initialized pointing to those retained subview objects. You need to release those in viewDidUnload. Are you doing that?
In viewDidUnload you should release all objects created from the NIB and set those pointer to nil. You should also release those same object in dealloc. Example here.

Should I release an UIImageView added to my view if I will detect touch on it later on?

I've learnt that if you create an object, you own it and need to release it when you're done with it. In this case I create an UIImageView and add it to my view like this:
myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image.png"]];
[myImageView setFrame:CGRectMake(10,10,100,100)];
[self.view addSubview:myImageView];
[myImageView release];
If I later on want to detect touch on myImageView like this in my touchEnded-method:
if([touch view] == myImageView){
NSLog(#"TOUCHED!");
}
This works, but is this correct, since I now used myImageView after releasing it?
And how do I release myImageView from the self.view that I added it to earlier?
The cardinal rule of Cocoa memory management is that you should retain those things you care about, and release those things you do not care about. There are a very small number of exceptions to prevent retain loops (delegates and datasources are never retained), but this is the rule you should follow.
In this case, if you are storing the image in an ivar, I would continue to retain it, regardless of the fact that its superview will always retain it, and so you don't "have" to. But if the view is removed from its superview, you will wind up with a dangling pointer, and then you will crash, so I code defensively against that by retaining. If you used an accessor here (which you should), then this would be automatic and much safer.
Apple has grown more consistent about this in iOS, switching their recommendation about IBOutlets. On Mac, you do not retain your IBOutlets, but in iOS, Apple explicitly instructs you to do so. This is similar to the situation you are discussing, and I agree with Apple's move towards a safer approach.
As long as your myUIImageView object has a retain count > 0, it will still exist and you can continue using it. When you first add it as a subview, it gets a retain message so it's retain count is likely 2. Then you send it release, so its retain count is reduced to 1. This means it still exists in memory. Now, if you sent it release again, or sent it removeFromSuperView then its retain count would be zero, and you'd lose it.
The behavior is erratic, sometimes you may see it works, sometimes you get a crash.
If you want to use that variable to point to your image view, retain the ivar (by using a retain property). This way you ensure that the image view is available for your controller class to use.
Assuming myUIImageView is an ivar of your custom UIView subclass, your code will work as long as your image view stays in his superview. The image view instance may be deallocated and you may end with an invalid pointer referencing a deallocated object. At best you crash.
According to memory management guidelines, you should consider your image view <-> custom uiview subclass relation as wether:
strong reference. You own the image view (as you created it), and are responsible for retaining / releasing it
weak reference. You don't own the object, thus keeping a reference to it may be dangerous.
in your case, it's probably a strong reference. your myUIImageView ivar should be a nonatomic retained property of your object.
If you need to access your UIImage at some point in the future, you need to retain it.
How you do this is at your discretion, but you should not rely on the UIView to retrain your objects for you. The situation you've created works for now, but it's fragile.

Properly releasing UITableViewController and UITableView directly added to a UIViewController

I have a UIViewController (parentVC) to which I add a UITableViewController as follows (it is not pushed since the tableview only occupies about half the screen):
tableVC = [[SmallTableVC alloc] initWithStyle:UITableViewStylePlain];
[self.view addSubview:tableVC.tableView];
In dealloc, I add [tableVC release];
Using instruments, I can see that the tableVC is indeed fully released when parentVC released, which is good, but I am not sure why as I thought that the UITableView of tableVC would have a retain count of 2 (1 for retention by tableVC and 1 for retention by parentVC). My intuition was that an additional [tableVC.tableView release] would be required, but adding it crashes the app. Why is tableVC released properly by the current code (if indeed it actually is)? Does a UITableViewController not retain its tableView? Thanks.
You are allocating tableVC, so retain count is 1. You do not pass tableVC to anything else, so there is no reason anything else would retain it. If something else did retain tableVC, it would be responsible for releasing it when it no longer needed it.
If you were to look at the retain count of the UITableView owned by tableVC it might be higher than one, as it is retained by both tableVC and, after you call addSubview, the view owned by parentVC.
Edit:
If you want to forcibly clean up tableVC.tableView then call removeFromSuperview before releasing tableVC. I believe that view controllers implicitly do this when released, but it does not hurt to do it manually as well.
After you call [self.view addSubview:tableVC.tableView] tableVC.tableView will have a retain count of at least two. One for the view hierarchy and one for tableVC. When you release tableVC it is removing the tableView from the view hierarchy which releases tableView, then it is releasing tableView itself. Even if it was not removing tableView from the view hierarchy, parentVC.view is about to be torn down and release the view hierarchy that included tableVC.tableView.
Only release what you have explicitly retained. The naming conventions make this pretty clear. If the name includes retain, new, alloc or copy then you should release the result.
Be careful trusting retain counts on iPhone. iPhone does not do real garbage collection and therefore these values should be ignored. Follow the memory management rules and you will be fine.
In this case, the tableVC will be released in "dealloc" and the view it controlled will be released in [super dealloc].
Do not trust retain counts!