iOS 7.1 removeFromSuperview crash
Asked Answered
U

9

14

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.

Unmusical answered 11/3, 2014 at 17:10 Comment(16)
I release subviews in the same way, and it hasn't broken with the iOS7.1 update. Perhaps check for if the subView is nil? (Even though this shouldn't technically be a problem)Exclave
Potentially troubling: contentVc sounds like a view controller (from the suffix), but you're treating it like a view. Is this intentional? What is that?Parody
Other than that, this code is fine; do you ever make a weak or unsafe reference to a view?Parody
how can subview be nil, if it is in the subviews array. ( but off subview is not nil I checked it. )Jersey
Why do you feel there is a need to remove all subviews when removing the view controller? It is not necessary at all.Viccora
contentVc is my main view controller. I'm adding new view controllers.view to contentVc.viewJersey
Can be the problem is, I trying to remove subviews, which are another view controller subviews?Jersey
This doesn't make any sense; if contentVc is a UIViewController then it doesn't have a subviews property. It just has a single view. Did you define subviews for this view controller?Parody
Sorry contentVc is a UIView, not a UIViewControllerJersey
Some things to keep in mind : I used to iterate through subviews like this to remove specific views, but at times I noticed, the for returned an object that was NOT a UIView. Ended up adding a if ([subview isKindOfClass:[UIView class]]). Not a good solution, but works. Secondly, If you are adding a viewController's view as a subview and attempt to remove it from superview, try logging something in that VCs viewWillDisappear/viewDidDisappear/dealloc methods to see if they are called. If yes, then check the code over there to see code that might be causing these conflict.Agitato
Check this #9252929 && iphonedevsdk.com/forum/iphone-sdk-development/…Fontainebleau
Does NSLogging(and comparing) the subviews when running iOS 7.0 and 7.1 help ?Illogical
@incmiko Is this resolved yet or issue persists?Spleen
this issue has been resolvedJersey
What had caused the issue ?Agitato
I will give a well deailed answer as soon as I have enough timeJersey
L
6

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.

Luisaluise answered 24/3, 2014 at 14:59 Comment(2)
Really thank you for your answer, but I've already solved them problem, as I wrote in a comment. I used the same mechanics for it: "I implemented a custom container, and I solved my random crash problem, with enable nszombie and use instruments. The problem was somethings are released twice, but the sad thing was, Xcode didn't told me where and what. And the crash wasn't directly after the line of the second release ran. It's crashed after a few seconds fully random. Now it works fine. thank you for your answer"Jersey
I voted up your answer, the only one thing is why I didn't deleted my question: it can be useful for somebody :)Jersey
B
7

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.

Beforehand answered 20/3, 2014 at 7:42 Comment(1)
my subviews are not nil, and not NSZombies. But if I try to remove it from superview it crashJersey
L
6

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.

Luisaluise answered 24/3, 2014 at 14:59 Comment(2)
Really thank you for your answer, but I've already solved them problem, as I wrote in a comment. I used the same mechanics for it: "I implemented a custom container, and I solved my random crash problem, with enable nszombie and use instruments. The problem was somethings are released twice, but the sad thing was, Xcode didn't told me where and what. And the crash wasn't directly after the line of the second release ran. It's crashed after a few seconds fully random. Now it works fine. thank you for your answer"Jersey
I voted up your answer, the only one thing is why I didn't deleted my question: it can be useful for somebody :)Jersey
S
2

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];
Sallysallyann answered 11/3, 2014 at 17:30 Comment(1)
you mean NSArray *subviews = [[contentVc subviews] copy];? otherwise it have no differenceTude
F
2

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.

Forage answered 24/3, 2014 at 2:25 Comment(3)
I implemented a custom container, and I solved my random crash problem, with enable nszombie and use instruments. The problem was somethings are released twice, but the sad thing was, Xcode didn't told me where and what. And the crash wasn't directly after the line of the second release ran. It's crashed after a few seconds fully random. Now it works fine. thank you for your answerJersey
And I think I should not using ARC, ARC is doing the same as you retain, release, its just counting instead of you. But I exactly know the concrete point from where I don't need the object, and I can release. I can write effective code. ARC is decide if the object reference count is 0, and release. I do the same, but an object lives until it have a strong reference ( retain ) or until the end of the brace, but I know that I don't need the object after a few lines of code.Jersey
ARC has more features than just releasing objects for you, it also enables auto-niled weak references, thus avoiding unsafe_unretained ones. Also it will be more efficient than you in most cases, and for the remaining ones you can use @autoreleasepool{}'s if desired.Forage
E
1

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.

Endarch answered 20/3, 2014 at 9:48 Comment(3)
the coolest thing is these subviews are not nil, but if I try to remove it crashJersey
it's not about nil value in the subview, it's about the super view, and please try to send a messageEndarch
UIView* view = [[UIView alloc]initWithFrame: self.view.frame]; [self.view addSubview: view]; [view release]; for (UIView* subview in self.view.subviews) { [subview removeFromSuperview]; [subview removeFromSuperview]; [subview removeFromSuperview]; }' it didn't crashEndarch
I
1

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. 

enter image description here

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)];
Illconditioned answered 26/3, 2014 at 8:33 Comment(2)
I must realse mainView, after removeFromSuperview, because it s a retained property of the viewController. The problem was not this. I've already solved my problem and I 'll answer my question as soon as I have enough timeJersey
this is wrong as the mainView is retained when it's add to the super view.Endarch
D
1

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

Debug answered 26/3, 2014 at 15:39 Comment(0)
I
0

Instead of:

for (UIView *subView in subviews)
     [subView removeFromSuperview];

Try:

[subviews makeObjectsPerformSelector:@selector(@"removeFromSuperview");
Idler answered 16/3, 2015 at 18:13 Comment(0)
J
0

Try checking if the view is != nil first before removeFromSuperview example:
@IBOutlet weak var btnSNSApple: UIView! if self.btnSNSApple != nil { self.btnSNSApple.removeFromSuperview() }

Janellajanelle answered 4/3, 2020 at 8:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.