This is a basic memory management code. I am dealing with an old app that does not have ARC implemented.
My question: Is it ok to access an instance variable after adding it to a view & releasing it.
In my opinion it is probably not right to access an instance variable after calling release on it, but can anyone advice ?
demoView = [[DemoView alloc] initWithFrame:[self demoRect:newType]];
[self addSubview:demoView]
[demoView release];
Later in code access it:
[demoView setBackgroundColor:[UIColor whiteColor]];
demoView.title = #"something";
object is released later on like this:
[demoView removeFromSuperview];
demoView = nil;
When this line is called, the retainCount of demoView is 1.
demoView = [[DemoView alloc] initWithFrame:[self demoRect:newType]];
after this line
[self addSubview:demoView]
retainCount gets increased to 2.
If nothing else is done but the demoView is released later in dealloc, or just removed, it will still have a retainCount of 1.
That is why the developer calls the
[demoView release];
to keep the retainCount at 1.
P.S.
In the older retain/release paradigm, you would have to maintain the retainCount and keep that in check. retainCount gets increased on alloc, on getting added to a UIView/NSArray/NSDictionary and on calling retain
And retainCount decreases when you call release, remove from a UIView/NSArray/NSDictionary.
When you add a subview to a view, subview is retained by view.
So, if you use a subview after you release a subview, it will work. But it is a really bad practice to use some object you had already released.
It's generally best to use accessor methods rather than directly manipulating ivars in manually reference counted code. Consider rewriting the implementation along the following lines:
self.demoView = [[[DemoView alloc] initWithFrame:[self demoRect:newType]]
autorelease]];
[self addSubview:demoView];
This way, if you remove the demo view from its superview you can safely keep it in memory should you ever want to add it back to the view hierarchy later, or move it to a different superview. In any case, the demo view should get a release message in dealloc so it can get cleaned up at the end of its owner's lifetime.
Related
I have a weird problem which causing crash, when I touch textfield quickly when my UITableviewContrller shows up.
It doesn't not always happen. maybe about 2 crashes of 10 tests.
It works fine previously, but I don't know why it sometimes crash now.
iPhone4 on iOS7.1.
crash message. It crash in OrderTableViewController. Before crash, I check that the retainCount is 1, so I don't know how this happen. I have no idea why it's deallocated.
2014-04-01 09:35:57.628 [17027:60b] *** -[OrderTableViewController respondsToSelector:]: message sent to deallocated instance 0x17fef520
Here's my code for pushing viewController.
I believe it's correct. I use it all the time.
OrderTableViewController *orderViewController = [[OrderTableViewController alloc] initWithNibName:#"OrderTableViewController" bundle:nil];
orderViewController.hidesBottomBarWhenPushed = YES;
[self pushViewController:orderViewController animated:YES];
[orderViewController release];
I don't declare textfield as retain. I'm not sure if it's the problem.
UITextField *uname;
UITextField *utel;
uname = [[UITextField alloc] initWithFrame:CGRectMake(180, 8, 120, 30)];
uname.BorderStyle=UITextBorderStyleRoundedRect;
uname.keyboardType=UIKeyboardTypeNamePhonePad;
uname.delegate=self;
utel = [[UITextField alloc] initWithFrame:CGRectMake(180-40, 8, 120+40, 30)];
utel.BorderStyle=UITextBorderStyleRoundedRect;
utel.delegate=self;
utel.keyboardType=UIKeyboardTypeNumberPad;
- (void)dealloc {
[uname release];uname=nil;
[utel release];utel =nil;
[super dealloc];
}
I tried profile by instrument with zombie template, but I can't find something useful.
Here're questions.
1. Any ideas for this issues?
2. How to workaround this kind of issues. I tried remove the code of [orderViewController release]; and retainCount become 2, it seems OK for 20 tests. But, it causes memory leak. how to reduced this leak issues. Could I release it in the navigation controller, when I don't need it? I know it's more complicated, but I don't know better way to workaround.
Form message of crash , it seems related to OrderViewController over-release, but I can't find the problem. Is there other problem causing the error message?
Thank you for your help.
don't use retainCount. It can never return 0, for example.
the problem is that your delegate is being deallocated before the view controller is done mucking with it. You could fix that by setting the delegate to nil in dealloc, but that may likely not be correct either in that it indicates that your view controller will live longer than the thing that it delegates behavioral decisions too.
your dealloc method is wrong; in non-ARC, you must call [super dealloc];
you should use ARC
First check to make sure you aren't calling dealloc directly on it.
Then try running the Xcode Analyzer as it might find the problem for you.
Then try to enable an Exception Breakpoint and see if it stops somewhere useful.
If those don't work try implementing the -retain and -release methods on the OrderTableViewController and then place breakpoints in each of them. You can then track who is retaining and releasing it and you should be able to find who is overreleasing it.
- (id)retain
{
return [super retain];
}
-(oneway void)release
{
[super release];
}
You should nil the delegate. That is, uname.delegate = nil in dealloc.
Most likely the problem is you're releasing your viewController before the Navigation Controller has a chance to claim ownership. There are two ways around this:
release your controller after pushing it to the Nav Controller
autorelease your controller before pushing it. If you do this, the active NSAutoreleasePool (which you don't need to worry about) will take care of releasing your controller at a later time.
[orderViewController autorelease];
My app didn't have any crash until iOS 7.1 came out. Now on any removeFromSuperview method, crash. For example: I got view controllers, and when I want to remove a view controller, I remove all of its subviews, and then remove from the stack (stack: I'm storing view controllers in this, for load new contents, and load previous contents):
for (UIView *subView in [contentVc subviews])
[subView removeFromSuperview];
And I got
-[CALayer retain]: message sent to deallocated instance
message
[actual removeFromParentViewController];
is a good way to remove it? And will it release the whole view controller and its subviews? Because instead of removeFromSuperview, my app doesn't crash. I don't understand what have been changed in iOS 7.1.
And how can I remove all subviews in a viewController without removeFromSuperview, and without remove my ViewController (if I just want to add new subviews, and remove the currently content)?
UPDATE:
sometimes crash for:
[myactualviewcontroller.view removeFromSuperview];
-[CALayer retain]: message sent to deallocated instance
Why???
and sometimes if I try to remove the main subview from the view controller view, its got the same crash:
[mainView removeFromSuperview] ( mainView is a single UIView, added to the vc.view )
UPDATE2: (well detailed)
so, I've got a container view. I'm adding a UIViewController.view to this container. And I'm adding a view as a subview to UIViewController.view. This view is not a local uiview, I mean, its declared as implementation{ UIView* mainView } .When my UIViewController will be deallocate, in its - (void) dealloc { [mainView removeFromSuperview]; [mainView release] [super dealloc];}
At the mainView removeFromSuperview my app crash.
It's usually not a good idea to modify an array while you're fast enumerating it. You appear to be using fast enumeration on a a view's array of subviews, and to be modifying that array at the same time (by removing subviews as you go). You could try something like this:
NSArray *subviewsCopy = [[contentVc subviews] copy];
for (UIView *subview in subviewsCopy) {
[subview removeFromSuperview];
}
However, as some others have mentioned, it's a little odd that you need to go to the trouble of removing these subviews manually. Under normal circumstances a view controller's view (and the view hierarchy under it) will be cleaned up automatically when the view controller itself is deallocated.
There are also some good tools available that can help you track down the source of the issue. In particular, you should profile your app (in Xcode, under the Product menu) and choose the Zombies tool when Instruments prompts you. With Zombies you can see the retain/release history of an object that was messaged after it was deallocated.
If you're attempting this manual cleanup of the view hierarchy because you suspect that your views will be leaked otherwise, I suggest that you also try the Leaks tool in Instruments and verify that when this code is disabled the relevant views are actually leaked.
Your program is crashing because you are releasing something more than once. That part is obvious.
The first step in finding it is to enable zombie detection in the debugger. (Project->Schemes->Edit Scheme->Diagnostics->Enable Zombie Objects). The goal here is to make your program crash sooner. This will drop you into the debugger as soon as you try to access a deallocated instance. Sometimes this will point you in the right direction, sometimes not, but it's always better to detect it as close to where the problem is as possible.
The next step is to use the Zombies instrument. This tool will give you more information than the previous step, but it's more complex to use (which is why I made it step 2 instead of step 1). The Zombies tool will keep track of all your allocations and releases, and detect when you try to access a zombie object.
The last resort is to start commenting out code. First comment out everything your program does between the time you create the view controller (the one that crashes) and when you release it. Then run the program and do whatever you need to do to make it display the bad view controller. It won't do anything, obviously, because it's just an empty view controller now, but it should not crash). Then start uncommenting blocks of code, a little bit at a time, and keep running it in between each iteration. This is a repetitive process, and can be tedious if your view controller code is large and complex. But the idea is to keep adding your code back in little by little until you add something back and it crashes - then you know you've found the piece of code that's causing the problem. You have to be creative here and choose carefully how you put your code back in - if your program has a nice modular design, you should be able to do this without much trouble. Spaghetti code will be difficult to do this with, but it might give you a good opportunity to restructure your code while you're at it. By going through this process, you'll narrow down the problem and eventually find the bug by process of elimination.
UPDATED
try to do this:
NSArray *subviews = [NSArray arrayWithArray:[contentVc subviews]];
for (UIView *subView in subviews)
[subView removeFromSuperview];
I think that you got the crash beacuse you're trying to fast enumerate an array that has variable length (in fact when you remove a subview, it is removed also from subview array).
If you want to remove the viewcontroller, just call:
[contentVc.view removeFromSuperView];
[contentVc removeFromParentViewController];
sometimes crash for:
[myactualviewcontroller.view removeFromSuperview];
You shouldn't add or remove a controllers' view from a view hierarchy manually but rather rely in UIWindow's rootViewController, push your controller to a UINavigationController, etc., to get the system to add the view to private underlying superviews. Unless your creating a Custom Container View Controller, which I guess you aren't.
If you just want to handle views manually don't use view controllers as they won't get retained by the system and they won't get any rotation messages, etc. So using a view controller is pointless in that case anyway.
As for subview memory handling, subviews are retained by their superview, so as long as you don't keep a strong reference, you don't need to release subviews, just remove a common superview.
Again, if you properly use view controllers just releasing the controller will get rid of all views.
Finally, you should start using ARC.
1.According to the Apple's documentation, calling removeFromSuperview will remove that view from superview and release it automatically.
So if you are using removeFromSuperview, then you should not call the [removedView release], which will crash your App.
Refer this screenshot from Apple.
In your dealloc implementation, you are having like so
- (void) dealloc {
// Removed from Parent view and released.
[mainView removeFromSuperview];
// no mainView exists in memory , so it crashed the App.
[mainView release];// Comment this line to avoid the crash
[super dealloc];
}
2.You should not mute the container that are being enumerated.
You are having like this,
for (UIView *subView in [contentVc subviews])
[subView removeFromSuperview];
Instead you can implement the same effect by having this one line from Apple.
[[contentVc subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
Please be sure that all of possible delegates removed before views deletion (i.e. someScrollViewDelegate = nil; before [someScrollView removeFromSuperview];) an/or animations are fully completed (all of CATransaction, [UIViev beginAnimation...], [UIView animateWithDuration...] etc.).
please do the following:
1- debug the for (UIView *subView in [contentVc subviews]) and check how many times it iterate.
if it doesn't crash in the first hit you can add this line before you remove the view
if (subView.superView != nil)
else try to make sure that you are not releasing the views twice somewhere else as it's
keep showing and will not crash till you remove it from it's superview.
UPDATE2:
i will consider that you are will aware of memory leaks, and that you have good experience in it.
whenever you add a subview to a view, this will retain the object by 1 in addition to the original 1, that will equal 2, then directly after adding the subview you have to release it, which will decrement the retain count back to one. here is the trick: you don't have to release the subview or remove it from it's parent view to get rid of the remaining retain count. you can simply remove or release the parent view. get the NSMutableArray As an example.
remove the [mainView removeFromSuperview]; from the dealloc: method. you can add it else where like viewWillDisappear: method. dealloc method shouldn't contain anything other than the release calls.
Instead of:
for (UIView *subView in subviews)
[subView removeFromSuperview];
Try:
[subviews makeObjectsPerformSelector:#selector(#"removeFromSuperview");
Try checking if the view is != nil first before removeFromSuperview
example:
#IBOutlet weak var btnSNSApple: UIView!
if self.btnSNSApple != nil {
self.btnSNSApple.removeFromSuperview()
}
I have a small question when programming objects in objective-C. I have an App that is just about complete and everything works fine. My question is that I set my objects to nil and release them at appropriate times.
But is this enough or when and where should I use removefromsuperview?
In the case of adding a UIButton to a UITableViewCell I add the UIButton with the following code:
UIButton *buttonReset = [UIButton buttonWithType:UIButtonTypeContactAdd];
buttonReset.frame = CGRectMake(250.0f, 7.0f, 75.0f, 30.0f);
[cell addSubview:buttonReset];
buttonReset addTarget:self action:#selector(resetSettings) forControlEvents:UIControlEventTouchUpInside];
buttonReset = nil;
[buttonReset release];
Do I also need to use
[buttonReset removeFromSuperview];
in this case?
buttonReset = nil;
[buttonReset release];
This doesn't make sense. You set a pointer to nil (null pointer) and then send a message to it. In most other languages this would result in a crash. In Objective-C it's allowed, but nothing will happen. You have to release before setting to nil. But you shouldn't do neither in this case, because buttonReset is an autoreleased object (you didn't use alloc/init to create it), so you don't own it and therefore you must not release it.
You also don't have to use removeFromSuperview in this case. You add a button (a subview) to your cell (the superview). The superview will hold a strong (retaining) reference of the button. When the cell is then released, it will also handle all of its subviews. You only have to remove it yourself when you actually want to do that, but not for memory management reasons.
If you didn't already know about it, you might want to consider using Automatic Reference Counting (ARC) in the future.
No, you should not call [buttonReset removeFromSuperview];, at least not right away: if you do, the button would disappear from screen (given the name of the method, this should come as no surprise). Moreover, you do not need to set your button to nil.
Calling removeFromSuperview is needed when you need the control to be dropped from the screen. If you also release it, the object representing your control would be destroyed. For example, if you added a button programmatically for a specific task, and have to remove that button once the task has been accomplished, calling removeFromSuperview is appropriate.
Calling removeFromSuperview on a view causes it to be removed from its superview. This will make the targetted view disappear from the screen with all the view it contains.
In your situation, I would just set the object to nil and be done with it.
See does removefromsuperview releases the objects of scrollview?.
There are interesting informations in it.
but it's worth digging deeper into this, because it's a very important
concept in ObjC. You should never call -release on an object you
didn't -retain explicitly or implicitly (by calling one of the Three
Magic Words). You don't call -release in order to deallocate an
object. You call it to release the hold you have put on the object.
Whether scrollview is retaining its subviews is not your business (it
does retain its subviews, but its still not your business). Whether
-removeFromSuperview calls -release is also not your business. That's betweeen the scrollview and its subviews. All that matters is that you
retain objects when you care about them and release them when you stop
caring about them, and let the rest of the system take care of
retaining and releasing what it cares about.
you should use just the
[buttonReset removeFromSuperview];
and then
buttonReset = nil;
as apple saying
If the receiver’s superview is not nil, the superview releases the receiver. If you plan to reuse a view, be sure to retain it before calling this method and release it again later as appropriate.
in UIView Referance
I can´t find where the problem is, but this code is crashing. Am I over releasing any object?
settings = [[SettingsViewController alloc] initWithNibName:#"SettingsController" bundle:nil];
settings.hidesBottomBarWhenPushed = YES;
NSArray * arrayWithRootController = [[NSArray alloc] initWithObjects:settings, nil];
[(UINavigationController*)([self.tabBar.viewControllers lastObject])setViewControllers:arrayWithRootController];
[arrayWithRootController release];
[settings release];
If I remove the line
[settings release];
The app doesn´t crash. but I am pretty sure its correct. May the problem be in another place?
Any ideas? Thanks a lot
You're not overreleasing in this snippet, but obviously something isn't right. Adding the root view controller to the array will retain it, but only for the life of the array. When the array dies, all the objects within it are released as well (my guess as to what's happening here).
Couple things to try:
First, make sure you're putting your array where you think you are:
[(UINavigationController*)([self.tabBar.viewControllers lastObject])setViewControllers:arrayWithRootController];
What do you expect [self.tabBar.viewControllers lastObject] to be? Are you sure this is where you want to be assigning your new array? If the receiver is invalid, your array won't be retained in your next line which means the view controller will also be released.
I'm not familiar with your architecture, but it appears you're assigning an array of view controllers to a view controller. self.tabBar is a navigation controller, and you can call setViewControllers on it. But self.tabBar.viewControllers lastObject... well presumably that's a view controller, but not neccessarily a navigation controller, it may not respond to setViewControllers (in which case it should crash, unless it's nil, which I'm guessing it might be).
Finally, try using the Instruments tool Zombies to see if you can pinpoint where the unintended release is coming from.
I have a View Controller as part of a navigation controller stack with two IBOutlets. In viewDidUnload I free them up:
- (void)viewDidUnload
{
self.myView1 = nil;
self.myView2 = nil;
[super viewDidUnload];
}
But I still had a leak. So I stuck release messages in the dealloc for them too:
- (void)dealloc
{
[myView1 release];
[myView2 release];
[super dealloc];
}
This appears to clear the memory leak. However, I was always told that I should only release ivars that I have created using alloc, copy or new. So I'm worried about these two releases being in here. Is this right or wrong? Can someone please explain this to me because I keep getting conflicting opinions... Thanks!
If any of your #property objects are declared retain or copy, you need to release them in dealloc. This includes your outlets.
By using IBOutlet, the variables are exposed to be connected in Interface Builder and allocated when the view controller is initialized. So they have to be released and deallocated, as the view controller is unloaded and deallocated. Since most IBOutlets are retained UI* properties, this is necessary.
Assigning nils to the variables is technically not deallocating. It's simply the last state to have retain count 0, just before actually being deallocated.
Also, please note they are referenced using self. It means, the references from the view controller becomes nil, not the allocations.
So in conclusion, IBOutlet properties must be released in dealloc()
(Though I am quite confident, someone else may provide 100% correct answer for this.)
The basic, safe pattern is
declare ivar
declare IBOutlet property for ivar
release property in dealloc
only reference property, never ivar
The xib sets the property, which release whatever might have been there first.
I'm a bit confused why that leak was there though. Setting the property to nil should release the old reference. Perhaps viewDidUnload wasn't even getting called? Are you sure you even need viewDidUnload?