iOS App Crashes while releasing a retained property - ios

I have a question. There may be a very simple solution to this question but I am not able to figure it out yet. If I use a property say #property(nonatomic, retain)UIView *mainView.
Now I synthesize it in .m file and release it in the dealloc method as following:
- (void)dealloc {
[mainView release], mainView = nil;
[super dealloc];
}
Then in my viewDidLoad, I'm allocating it and adding it as the subview of my self.view like following:
- (void) viewDidLoad {
mainView = [[UIView alloc] init];
.
.
.
[self.view addSubView: mainView];
}
Now I understand that at this point my mainView would have 3 reference counts (one from alloc, one because it's a retained property, and the third one when I added it to self.view), its parent controller would own it too.
Now, my question is if after adding my view to self.view, I release my mainView using
[mainView release];
My app crashes when I go back to the previous view as I am sending release to already deallocated object. Now my question is how am I overreleasing my view here. what am I missing because when I use following code it works fine and no crashes occur.
- (void) viewDidLoad {
UIView *newView = [[UIView alloc] init];
self.mainView = newView;
[newView release];
.
.
.
[self.view addSubView: mainView];
}
I know why this second viewDidLoad method works but what I dont know is why first one fails, I am supposed to release my view after adding it to self.view. Right?
NOTE: I understand that in the first viewDidLoad, I can use autorelease method to release the view that is being assigned to the ivar and it wont crash but the whole point is I am trying to reduce the use of autorelease as much as possible. And I am not using ARC at all
I would really appreciate the explanation and suggestions.

From your question:
Now i understand that at this point my mainView would have 3 reference
counts (one from alloc, one coz its a retained property and the third
one when i added it to self.view)
You didn't assign through the property but assigned directly to the instance variable, so there was no retain; only in ARC does assigning to the instance variable retain the value. So, do not perform the manual release.

In order for your #property to retain the mainView, you should use it as self.mainView and not just mainView. If you use the latter alone, it will not retain it. Basically if you call self.mainView = ... it is calling a setter method for mainView which does a [mainView retain]; internally. When you are directly assigning it, it wont execute this setter and retain will not be executed.
You should try it like this,
self.mainView = [[[UIView alloc] init] autorelease];
[self.view addSubView:self.mainView];
or as shown in your question.
UIView *newView = [UIView alloc] init];
self.mainView = newView;
[newView release];
[self.view addSubView:self.mainView];
You can also try using ARC for your project. Your code will look like this in ARC,
self.mainView = [[UIView alloc] init];
[self.view addSubView:self.mainView];
Check the documentation for more details.

In your first viewDidLoad method you are not referring to self.mainView only mainView thats why its not retained, in order to retain property work you have to set mainView using self.mainView!

Related

Why one weak reference gets deallocated and other doesn't?

I am getting used to using weak and strong references and when to use them and when not and I got to a case like described below (check the comment about the warning)
#interface MPIViewController ()
#property (weak, nonatomic) UIView *subview;
#property (weak, nonatomic) UILabel *label;
#end
#implementation MPIViewController
// ...
// some code here
// ...
- (void)viewDidLoad
{
[super viewDidLoad];
self.subview = [[UIView alloc] init]; // warning here: assigning retained object to weak property
self.label = [[UILabel alloc] init]; // no warnings
[self.view addSubView: self.subview];
[self.view addSubView: self.label];
}
// ...
// some code here
// ...
#end
From description of - (void)addSubview:(UIView *)view:
This method establishes a strong reference to view and sets its next
responder to the receiver, which is its new superview.
This means that this object won't be deallocated after method finishes as it's superview will retain it and hold a strong reference to it and therefore this view will be kept in memory for as long as its superview is there. Am I right here?
I am not sure also if I understand assigning here correctly. Warning says that it will be deallocated straight after the assignment but this sounds wrong as then it wouldn't be possible to assign any variable to a weak pointer as it would get deallocated in the next line of code?
For UILabel same assign works fine, however for UIView it doesn't? Does the compiler treat UIView somehow differently? This really puzzles me how that is even possible.
This code can be fixed easily just by assigning the UIView to a local method variable and then passing it to the setter like this:
UIView *tmpView = [[UIView alloc] init];
self.subview = tmpView;
Variables declared in the method are by default strong so having such a construction removes the warning as the compiler thinks that this variable has a strong reference so weak reference that is then assigned to will be kept as long as the method variable will point to it. BUT! how does that make sense as the tmpView is only a local method variable and will be dumped after method will finish?
To the first question:
Let's have a closer look to it:
self.subview = [[UIView alloc] init];
[UIView alloc] returns an instance with ownership +1. This is assigned to a (non-visible) strong reference, which build the self of -init. -init passes the ownership through. (This is not correct, if -init returns an instance which is not the original receiver, but for your case it is enough details.) So we can think of the return value of -init as an ownership transfer, too.
You assign this instance to a weak variable. In this moment it can be released. (Read: ARC does not promise to do it immediately, IIRC.) the instance variable can be nil before the object is hold by its superview. So this code is dangerous:
self.subview = [[UIView alloc] init];
// _subview can be nil'ed
[self.view addSubView: self.subview]; // add nil
I do not believe that this is your problem, but it can be a problem. – Thinking again about it, it is your problem. Read the edit at the end. –To get rid of it, simply use a strong local variable:
UIView *subview = [[UIView alloc] init]; // defaults to __strong
[self.view addSubView: subview]; // adds an ownership
self.subview = subview;
The second question:
I do not know, why the compiler gives you no warning in the second case. What does happen, if you repair the first case?
At runtime a different handling of both cases is possible, because it is undefined, when the first instance is released. Maybe as a part of optimization a pointer is reused. More detailed:
__strong id completlyHiddenCompilerGeneratedVar;
… = [(completlyHiddenCompilerGeneratedVar=[UIView alloc]) init];
… = [(completlyHiddenCompilerGeneratedVar=[UILabel alloc]) init];
The first instance would be dealloc'ed, when the second instance is created, because it overwrites the internal strong reference.
Again: Repair the first case and tell us, what happens with the second one.
An object need at least one strong pointer to it in order to be kept in memory.
So when you alloc it to a weak pointer that condition is not being met. Make your properties strong if you really need to access these views.

Why a UIViewController would stay in memory

Assuming that a view controller is created like this:
#property (nonatomic, strong) SomeViewController *someViewController;
...
self.someViewController = [[SomeViewController alloc] initWithView:imgView];
[self addChildViewController:self.someViewController];
self.someViewController.view.frame = self.view.bounds;
[self.mainView addSubview:self.someViewController.view];
Why would it not get released by the following?
__weak MainViewController *weakSelf = self;
self.someViewController.didCloseBlock = ^{
[weakSelf.someViewController.view removeFromSuperview];
[weakSelf.someViewController willMoveToParentViewController:nil];
[weakSelf.someViewController removeFromParentViewController];
weakSelf.someViewController = nil;
};
I can tell it's not getting released because if I keep opening and closing the view controller (creating a new instance each time I open one), it causes low memory warnings (and then a crash on iOS5), and in SomeViewController didReceiveMemoryWarning, I see a log for the number of times I've created a new SomeViewController. For example, when I get the memory warning after opening 9 new SomeViewControllers, I will get 9 didReceiveMemoryWarning logs, indicating that I have 9 SomeViewController instances in memory, even though I'm nilling each one out in the code above.
You're retaining your view once in your property with the strong annotation and again with self.someViewController = [[SomeViewController alloc] initWithView:imgView];
Using the synthesized variable should get rid of this:
_someViewController = [[SomeViewController alloc] initWithView:imgView];
If you're not using ARC, you can use self.someViewController = [[[SomeViewController alloc] initWithView:imgView] autorelease];
I'd probably go for the first option, ARC or not though.
You are just setting the block didCloseBlock, nothing else actually. Do you execute it?

Memory issues because of UIDatePicker

- (void)viewDidAppear:(BOOL)animated
{
[super viewWillAppear:animated];
_datePicker = [[UIDatePicker alloc] init];
_datePicker.datePickerMode = UIDatePickerModeDate;
_datePicker.frame = CGRectMake(70, self.view.frame.size.height ,250,100);
_datePicker.alpha = 0;
[_datePicker addTarget:self action:#selector(saveDate:) forControlEvents:UIControlEventEditingDidEnd];
[self.view addSubview:_datePicker];
}
I'm trying to figure out where is my "unbounded memory growth" , I'm using ARC in my app. I have a few memory issues in my app and I'm trying to figure them out one by one.
While moving between 2 viewControllers I can clearly see a big increase in memory use, the main cause for it is the code I wrote here. What am I doing wrong, how can I release it and where?
Thanks
viewDidAppear: gets called every time the view appears (even if the controller has been initialized already). So each time the view appears you are allocating a new UIDatePicker without releasing the old one.
If your datePicker is defined as a #property with "retain" then I would use
self.datePicker = [[[UIDatePicker alloc] init] autorelease];
By using self.datePicker you are calling the synthesized setter which will automatically release the old value for you.
Alternatively you can move this initialization to initWithNibName: or viewDidLoad: instead, that way it will only get called once.
Try this out and see if it helps your memory.
(I'm assuming you're not using ARC, otherwise what I said won't really help you).

iOS ZXingWidget - use view of ZXingWidgetViewController in own ViewController.view as subview

I am trying to use the iOS zxing Widget for QR Code Scanning. I have a ViewController which is pushed as an Item in my UINavigationController or presented Modally from another ViewController. This ViewController has a SegmentedControl for 3 different views. Two of those Views are UIWebViews which load simple Websites, nothing special about them.
The selection looks something like this:
- (IBAction)segmentedControlValueChanged:(id)sender {
NSString *urlString;
ZXingWidgetController *widController;
QRCodeReader* qrcodeReader;
NSSet *readers;
switch (segmentedControl.selectedSegmentIndex) {
case 0:
[self.view bringSubviewToFront:self.productSearchWebView];
urlString = [[SACommunicationService sharedCommunicationService] getURLforKey:kURLTypeProductSearch];
[self.productSearchWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]];
break;
case 1:
[self.view bringSubviewToFront:self.marketSearchWebView];
urlString = [[SACommunicationService sharedCommunicationService] getURLforKey:kURLTypeMarketSearch];
[self.marketSearchWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]];
break;
case 2:
widController = [[ZXingWidgetController alloc] initWithDelegate:self showCancel:YES OneDMode:NO];
qrcodeReader = [[QRCodeReader alloc] init];
readers = [[NSSet alloc] initWithObjects:qrcodeReader,nil];
widController.readers = readers;
[self.QRCodeScannerView addSubview:widController.view];
[self.view bringSubviewToFront:self.QRCodeScannerView];
break;
default:
break;
}
}
I tried to debug and go step by step and find out where the problem comes from:
Decoder (which is part of the underlying ZXing logic) tries to call "failedToDecodeImage:" from its delegate (which should be the ZXingWidgetController class) and crashes (EXC_BAD_ACCESS)
While stepping through I found out that the "cancelled" Method of the ZXingWidgetController gets called. Now I don't really know why this method gets called. The Widget shouldn't stop right after initializing and starting the decoder.
So the answer is a very simple one.
I was using iOS 5.0 and ARC. The ZXing ViewController is instantiated locally inside the method. Since the ViewController itself does not get viewed ARC sets a release at the end of the method and the ViewController gets freed. Since the ViewController is released, the view which was retained by the ViewController will be released as well. Cancelled is called because the Main ViewController does not exist anymore and calling some method on a nil pointer results in a BAD_ACCESS.
The Solution here was to set the ZXingViewController as a global strong property. This kept the object from being released right at the end of that method and thus the view which was added as a subview to another ViewControllers view was held in memory as long as the ViewController was alive.
You're not supposed to add controller views as subviews of another view. You're suposed to present controllers using the various UIViewController mechanisms.
By doing what you're doing, you're violating UIViewController contracts. The widget isn't getting things like viewWillAppear, viewDidAppear, etc.
If you want to use ZXing at the UIView/CALayer level instead of the UIViewController level, look at the classes in the ZXing objc directory.
Try this... also in the .h file make this ZXingWidgetController *widController;
And also to the viewScanner set clipToBounds to true.
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self performSelector:#selector(openScanner) withObject:nil afterDelay:0.5];
}
-(void)openScanner
{
self.widController = [[ZXingWidgetController alloc] initMiniWithDelegate:self showCancel:NO OneDMode:YES];
NSMutableSet *readers = [[NSMutableSet alloc ] init];
MultiFormatReader* reader = [[MultiFormatReader alloc] init];
[readers addObject:reader];
self.widController.readers = readers;
[viewScanner addSubview:self.widController.view];
}

iOS SDK: ARC removeFromSuperview

I have a simple question regarding ARC. I show a UIView if a user taps a button using addSuperView within a UIViewController. The UIView contains a close button, if tapped I want to remove the view.
I used to call a method within the UIViewController after animating the view offscreen:
- (void)viewDidClose:(UIView *)view
{
[view removeFromSuperview];
[view release], view = nil;
}
Now using ARC I changed it to:
- (void)viewDidClose:(UIView *)view
{
[view removeFromSuperview];
view = nil;
}
The question now is: I want to remove the protocol and the delegation to the view controller and do this within the UIView itself.
Pre-ARC (within view):
- (void)didStop
{
[self removeFromSuperview];
[self autorelease];
}
I can't use 'autorelease' in ARC nor set 'self = nil', as far as I know ARC comes in place as soon as I set the view to nil or replace it, but what if I don't replace it? Is [view removeFromSuperview] enough to take care of everything or does this leak?
Thanks a lot! I appreciate any help!
(Note that in your non-ARC version, you'd want the autorelease before the removeFromSuperview).
I've wondered about similar things. The 'danger' is that when you do the removeFromSuperview without first doing an autorelease, the object is immediately deallocated, even before the end of the method. That's skeezy.
My idea was that you'd make the method return self. In the non-ARC case, you'd create this pointer with autorelease but ARC should do that for you. You may not need the pointer in the caller, but I believe this will force ARC to do what you want.

Resources