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
Related
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 always wondered what are the best practices while working with UIImageView objects and a will give you a few examples about what I'm unsure.
First of all I am working on a turn based game that supports multiple matches at the same time, and certain views (a background image, label and a few buttons) will be loaded to self.view very often. What is the best way to display them, add them and then remove:
[self.view addSubview:view];
[view removeFromSuperView];
Or is the best way to add them and play with the hidden property (show and hide whenever i need, even in different matches)?
Another question is do I need to set an UIImageView to nil after I remove it from superview?
And the last question is: If I have a UIView class that I load to an UIImageViewController and want to release/remove it from within [self removeFromSuperView] is enough to release all the memory occupied by that view class?
If those views are loaded often into the screen, the best approach is to hide them instead of removing them. I'll remove them when I'm not using them anymore.
Removing a view from the superview reduces the retain count of the object in 1. If you are using ARC, you shouldn't worry about it, if you are not, be sure that the retain count is 0 after removing it (+1 for every alloc, add to subview, and -1 for every release, autorelease, removeFromSuperView). If after removing the view the retain count still 1, you can do = nil to release it. If the retain count of an object is 0, then the system will free it.
Same as 2.
Regarding points 2 and 3, it really depends on the scope of your UIImageView. If you declared it as a strong property, then you will have to set it to nil in order for ARC to release the memory. If it just a variable inside a method, then at the end of the execution of the method body, the variable will be released anyway (and be retained only by the view hierarchy).
I am using a UISwitch in in the menu of a Cocos2d game I am making.
After the scene is changed (the user clicks any button and goes to a different scene/layer the switches are still on the screen
I have tried setting switch1 = nil; and switch2 = nil;in my dealloc but that doesn't work. How would I do this?
Edit: Also tried [switch1 release]; which didn't work either
You generally remove controls from a view with:
[switch1 removeFromSuperview];
If you have any strong references to that control elsewhere, you can then do the appropriate memory management to release the switch at that point (e.g. setting it to nil or releasing it, as appropriate). You'd have to tell us about how it's defined (strong or weak, property or ivar, IBOutlet or programmatically created, etc.) for us to provide counsel there, but you probably have that covered.
Im struggling to solve in a very clean way a problematic involving memory overload (management).
Im having a serie of view that include other views, in my project I have a situation like this:
MainView
|_PageView
|_CustomButton
soo far soo good, easy as a cake. CustomButton have a delegate (protocol) in it for some reasons, so we have in PageView a "for cycle" that creates N CustomButtons, set the delegate as self in PageView (PageVew extend CustomButtonDelegate) and release the buttons afer attaching them like
{
CustomButton *customButton_ = [[CustomButton alloc] initWithFrame:CGRectMake(100.0,50+(i*55.0),200.0);
customButton.delegate = self;
[self addSubView:customButton_];
[customButton_ release];
}
soo far soo good again. Button will be press, PageView get the protocol method, do some code and voilà, done. One problem is that at one point, MainView must remove PageView, so In a method I call
[pageView_ removeFromSuperview];
[pageView release], pageView_ = nil;
pageView_ = [PageView alloc] initWithFrame.....];
and I recreate the object with other data to display.
I noticed that PageView never gets release and removed from the memory because its retainCount is exactly how many CustomButton I created inside PageView and assign the delegate to self plus one of course. My question is, what is the cleanest way to remove safely all the objects and be able to remove PageView too, free the memory (because Im loading a quite large amount of data to display in it) ?
Right now i'm doing:
Create in PageView a NSMutableArray, that I CustomButton the objects in
it, and before to remove PageView, I cycle it and set the delegate = nil and then release
each object, after I release the NSMutableArray (called "holder").
But the problem is that if I want to add more objects of different types with other protocols, adding to this array, can lead to other problems of retaining the objects.
Where do I lack guys, knowledge so I need to study more (quite sure I can say) or do I need to approach with another OOD?
Thank you guys, im going overload with this problem and my brain is stuck in a close road. :)
Looks like your CustomButton's delegate is a retain property of CustomButton. Delegate should be an assign property, not retain nor copy. See here.
Given (arbitrarily):
CGRect frame = CGRectMake(0.0f, 0.0f, 100.0f, 30.0f);
What's the difference between the following two code snippets?
1.
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = frame;
2.
UIButton *button = [[[UIButton alloc] initWithFrame:frame] autorelease];
I think they're equivalent. Haha! Trick question you sneaky little punk!
Reasoning
-buttonWithType: returns an autoreleased UIButton object.
+[NSObject alloc] defaults scalar instance variables to 0, so buttonType should be 0, or UIButtonTypeCustom.
Pros & Cons
You could argue that it's clearer to use -buttonWithType: and set buttonType explicitly and that it's safer in case Apple changes UIButtonTypeCustom to be 1 instead of 0 (which will most certainly never happen).
On the other hand, you could also argue that it's clear & safe enough to use -initWithFrame. Plus, many of the Xcode sample projects, such as "TheElements" & "BubbleLevel," use this approach. One advantage is that you can explicitly release the UIButton before the run loop for your application's main thread has drained its autorelease pool. And, that's why I prefer option 2.
I would strongly suggest using the first approach (+buttonWithType), because that's the only way to specify the button type.
If you +alloc and -initWithFrame:, the buttonType is set to some standard value (not sure which, and this could change in later versions of the SDK) and you can't change the type afterwards because the buttonType property is read only.
The main (maybe the only) difference is in memory management: as you said, buttonWithType returns an autoreleased UIButton. This way you don't have to worry about releasing it. On the other hand, you don't own it, so you cannot release it when you want to (except, of course, if you drain the autorelease pool).
Calling explicitly [[UIButton alloc] initWithFrame:frame], instead, you dynamically alloc your button, so you own it and you're responsible for releasing it. If you plan to retain your button for some reason then maybe you should consider the second solution, but if, as in this case, you are autoreleasing it immediately, there's no big difference between the two way of creating a button...
Two options are the same but I prefer option 2 because it can handle memory management
You can use the first and do "[button retain];", so you will never lose the pointer.
I've went through the UIButton documentation, and here what i've found:
Discussion
This method is a convenience constructor for creating button objects with specific configurations. If you subclass UIButton, this method does not return an instance of your subclass. If you want to create an instance of a specific subclass, you must alloc/init the button directly.
I guess this is the trick. The alloc-initWithFrame is for subclasses.