managing subViews in iOS - ios

I am creating a Custom Grid kind of View for my App. I am placing many subviews on them. I have a necessity to reload the Gird with new set of Views based on users' interaction and when new data arrives.
I allocate memory for my subviews as this:
while(index < count)
CustomGridTile *view = [[CustomGridTile alloc] initWithFrame:frame];
[self addSubView:view];
[view release];
}
When I wanted to refresh my Grid, I remove all subviews from their superViews and create new set of Grid Tiles (subviews), add to the Custom Gird View and release them.
Am I doing things correctly? Can this bring me memory related issues?

As long as you are removing the subview it should release memory.
Explanation:
When you allocate the view CustomGridTile its retain count becomes 1. When you add it as the sub view, its retain count would become 2. When you release, retain count becomes 1. Finally when you remove the subview, its retain count will become zero and should be released.

releasing the views can bring some issues - i suggest attempting an autorelease on them or something along those lines
otherwise what i would do is when you remove it from the superview release it as well, but releasing after you add it as a subview has given me troubles in the past

Related

How to properly "clean" a UIView that was removed from Superview in Swift? - IOS

I guess i might not understand how memory deallocates in Swift properly and thats why i wanted to ask this:
If i create a Message to the user using a UIView with a Label. i show it with an animation and then use ".removeFromSuperview()". when does it's memory deallocates? what are the conditions for it to deallocate?
Im asking because if it doesn't deallocate until the app closes - it means that for the run of the app - each message shown and then hidden will take up memory for no good reason.
Thanks for anyone who explains :)
Views maintain a strong reference to their subviews. Once the subview is removed, the superview relinquishes this reference. If you have no other strong references the retain count will decrement to zero and the view will be released.

iOS 7.1 removeFromSuperview crash

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()
}

UIView subviews' order?

If i use a for loop;
for(UIView *subview in [myView subviews])
in which order, subviews are brought?
According to their added order or something else?
As the documentation says, it reflects their visible order in the screen.
#property(nonatomic, readonly, copy) NSArray *subviews
Discussion
You can use this property to retrieve the subviews associated with
your custom view hierarchies. The order of the subviews in the array
reflects their visible order on the screen, with the view at index 0
being the back-most view.
For complex views declared in UIKit and other system frameworks, any
subviews of the view are generally considered private and subject to
change at any time. Therefore, you should not attempt to retrieve or
modify subviews for these types of system-supplied views. If you do,
your code may break during a future system update.
I find it to be useful, and in my experience it places subviews in order that they were added. Just be careful using them when using UIScrollView. I occasionally ran into 'phantom' subviews that are created for scroll bars

UIImageView best practices

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).

When do I use 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

Resources