I have a page with a back button that takes me to an inbox. I do this in viewWillDisappear -
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[inboxToolbar removeFromSuperview ];
[inboxToolbar release];
}
If I do this a few times, the app crashes and the exception thrown is "unrecognized selector sent to instance". This happens very intermittently. Any thoughts on how to go about fixing / debugging it?
Assuming the crash is accessing your inboxToolbar ivar, change your code to the following:
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[inboxToolbar removeFromSuperview ];
[inboxToolbar release];
inboxToolbar = nil;
}
This will ensure inboxToolbar does not keep a pointer to a deallocated object. You could also have other issues, would need to see the crash log and more of your code to be sure.
add an exception break point to see where it is being thrown, you probably are sending a message to an object that isn't of the class that you believe it to be, after you have stopped on the exception breakpoint, you can hover over the variable to see what classes they belong to.
if that doesn't help post the actual message and perhaps we can help more, could be iOS version issue, etc.
Try checking whether it will respond to your method first...
if ([inboxToolbar respondsToSelector:#selector(removeFromSuperview)]) {
[inboxToolbar removeFromSuperview];
}
Related
I have Simple popOver controller in my project. i can dismiss it using action button with following code.
[[self presentingViewController] dismissViewControllerAnimated:YES completion:nil];
but sometimes app gets crash if i do something on that viewController
before dismiss it.
Example : There is a pickerView for selection and post button. but without posting it if i did some selection and cancel it im getting this error. its only happening occasionally. not everytime. if it happens onetime then it might happen everytime after that.
Can someone explain? is it memory leak or something?
I think have solved the problem . in that PopOver new window i was using locationManager and i was using just
[locationManager stopUpdatingLocation];
but i have added following thing as well
locationManager = nil;
no crash anymore
Sometimes if you have dealloc function as mentioned below, this issue may occur.
- (void)dealloc {
[_yourview release]; //don't do this
[super dealloc];
}
I hope this might help some one else
I am seeing some random crashes with my app (although not reproducible when I run through same steps). I am observing the contentOffset property of the scrollview to take some action when it changes.
But I am getting below exception (randomly) with my below code of KVO registration and de-registration.
Is there any safe check that can be applied here.
*** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <MyPagingController 0x1f05e460> for the key path "contentOffset" from <UIScrollView 0x1f0a8fd0> because it is not registered as an observer.'
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.scrollView addObserver:self forKeyPath:#"contentOffset" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
}
- (void)viewWillDisappear:(BOOL)iAnimated {
[super viewWillDisappear:iAnimated];
[self.scrollView removeObserver:self forKeyPath:#"contentOffset"];
}
Your unsubscribing code somehow gets hit more often than the subscribing code. Unfortunately KVO does not handle this nicely and the failure throws an exception rather than just doing nothing as you would expect. You either need to make sure it only gets hit once, or at least catch the exception like this:
#try
{
[self.scrollView removeObserver:self forKeyPath:#"contentOffset"];
}
#catch (NSException * __unused exception) {}
}
As much as I love KVO, this need to balance observing and removing is a real issue. You will also get crashes if you nil an object that was KVO observing without telling the sending class to de-register you and the class then tries to send an update to the receiver object. Oops! Bad Access!
As there is no official way to ping to see if you have added an observer (which is ridiculous given that a huge part of what goes on in OS is based on KVO), I just use a simple flag system to ensure that nothing gets added more than once. In the example below, I have a bool flag to track the observer status.
#interface RepTableViewController ()
#property (nonatomic, assign) BOOL KVOSet;
#end
#pragma mark - KVOObserving
- (void)_addKVOObserving
{
//1. If the BOOL flag shows we are already observing, do nothing.
if (self.KVOSet) return;
//2. Set the flag to YES BEFORE setting the observer as there's no guarantee it will happen immediately.
self.KVOSet = YES;
//3. Tell your class to add you up for the object / property you want to observe.
[[ELRepresentativeManager sharedManager]addObserver:self
forKeyPath:#"myRepresentative"
options:NSKeyValueObservingOptionNew
context:nil];
}
- (void)_removeKVOObserving
{
//1. Do nothing if we have not set an observer
if (!self.KVOSet) return;
//2. If we have an observer, set the BOOL flag to NO before removing
self.KVOSet = NO;
//3. Remove the observer
[[ELRepresentativeManager sharedManager] removeObserver:self
forKeyPath:#"myRepresentative" context:nil];
}
Yes, using flags to check state is frowned upon by the coding-police. But there really is no other way to be sure except to bean count.
Whatever you do, remember that some classes need to observe even after viewWillDisappear is called (but the view still exists somewhere in the hierarchy), so you can't just do the observe/remove from viewWillAppear/WillDisappear trick. You may need to use delegate callbacks to the object that created the view (and will destroy it) to ensure you never leave a KVO calling class hanging. Having said that, I'm no expert in this stuff, so I'm sure there are people who can do this with way more finesse than my patented bean-counting lol
In iOS, I pop from current viewController into previous one, but it doesn't go into dealloc.
Is this because there is another pointer pointing towards the current viewController, either in a different viewController or in the current one?
This is where I pop to previous view:
- (IBAction)fileUploadCancelTouched:(UIButton *)sender {
[self.fileToUpload cancel];
[self.view hideToastActivity];
[self.greenprogressBar removeFromSuperview];
[self.subView removeFromSuperview];
self.fileUploadCancelButton.hidden = YES;
if (self.commandComeBackToFinalScreen == 1) {
[self.navigationController popViewControllerAnimated:YES];
}
}
This is my dealloc function:
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
self.greenprogressBar = nil;
self.fileUploadCancelButton = nil;
self.fileToUpload = nil;
[buttonHome_ release];
[buttonTestMeAgain_ release];
[buttonMarkMyTest_ release];
[examId_ release];
[sender_ release];
self.ob = nil;
[_fileUploadCancelButton release];
[super dealloc];
}
Check to make sure that ARC is not enabled in your project. If it is not ARC enabled then dealloc should be called unless your code is retaining your view controller. You should check through the Instruments tool if your pop commands reduces memory or not.
There may be some other reasons as mentioned in another answer that I am posting below:
The obvious reason is that something is retaining your viewController. You will have to look closely at your code. Do you do anything that in your class that uses delegates, since they sometimes retain the delegate. NSURLConnection will retain your class, and so does NSTimer. You can scatter code in you class and log your class's retain count, and try to find out where. In the code you showed so far the retain could should just be 1, since the class is only retained by the navigation controller.
Also, before you pop your view, get a reference to it, pop it with NO animation, and then send it some message that has it report the retain count (this would be some new method you write). That new method could also log other things, like whether it has any timers going, NSURLConnections, etc.
First of all, get rid of [super dealloc]. I know that's intuitive, but the documentation says don't do it.
In my own case, I had an observer & timer in my dealloc method, but that wouldn't run since the timer had a strong pointer to the controller.
Created a dedicated clean up method which removed the observer & invalidated the timer. Once that ran, the controller was correctly deallocated.
This is used to work fine for my pre-ARC code, but since refactoring all the project to be ARC compatible, I start getting this crash:
[CustomViewController respondsToSelector:]: message sent to deallocated instance
My project is an iPad app with a split view, but contrary to apple documentation, previous developer has put another view controller to be visible on app launch before split view. So I know this is not the right way to do, but as I said it used to work before ARC integration so I need to get a workaround with this.
The root view controller which contain a menu of items, each item display a detail form to be filled, then a click on next button move to the next detail screen, etc.
The issue starts when I click on home button put on root view to get back to the home view, here is the relevant code that move the user to the home screen:
//this method is in the appdelegate, and it gets called when clikc on home button located on the root view
- (void) showHome
{
homeController.delegate = self;
self.window.rootViewController = homeController;
[self.window makeKeyAndVisible];
}
Then when I click on a button to get back to the split view (where are the root/details view), the app crashes with the above description. I profiled the app with instruments and the line of code responsible of that is located in the RootViewController, in the didSelectRowAtIndexPath method, here is the relevant code:
if(indexPath.row == MenuCustomViewController){
self.customVC=[[CustomViewController alloc] initWithNibName:#"CustomVC"
bundle:nil];
[viewControllerArray addObject:self.customVC];
self.appDelegate.splitViewController.delegate = self.customVC;
}
customVC is a strong property, I tried to allocate directly and assign to the instance variable but that didn't help to fix the crash. Any thoughts ?
EDIT:
Here is the stack trace given by instruments:
[self.appDelegate.splitViewController setViewControllers:viewControllerArray];//this line caused the crash
[viewControllerArray addObject:self.appDescVC];//this statement is called before the above one
self.custinfoVC=[[CustInfoViewController alloc] initWithNibName:#"CustInfo" bundle:nil];//this statement is called before the above one
self.appDelegate.splitViewController.delegate = self.appDescVC;//this statement is called before the above one
custinfoVC and appDescVC are strong properties.
I solved this problem by setting my delegates and datasources to nil in the dealloc method. Not sure if it'll help you but its worth a try.
- (void)dealloc
{
homeController.delegate = nil;
//if you have any table views these would also need to be set to nil
self.tableView.delegate = nil;
self.tableView.dataSource = nil;
}
You may want to setup the CustomViewController during app launch, and display the other views modally on top if necessary. The error message you're getting is because something is getting released by ARC prematurely. It might have not manifested before because pre-arc stuff wasn't always deallocated immediately. ARC is pretty serious about releasing stuff when it leaves scope
Hard to tell without seeing a lot more of the code involved, and what line it breaks on, etc.
This:
- (void) showHome {
//THIS: where is homeController allocated?
homeController.delegate = self;
self.window.rootViewController = homeController;
[self.window makeKeyAndVisible];
}
EDIT:
Add this line right above the line that causes your crash
for (id object in viewControllerArray) {
NSLog(#"Object: %#",object);
}
I had the same Problem.If you are not using "dealloc" method then use "viewWillDisappear" to set nil.
It was difficult to find which delegate cause issue, because it does not indicate any line or code statement for my App So I have try some way to identify delegate, Maybe it becomes helpful to you.
1.Open xib file and from file's owner, Select "show the connections inspector" right hand side menu. Delegates are listed, set them to nil which are suspected.
(Same as my case)Property Object like Textfield can create issue, So set its delegate to nil.
-(void) viewWillDisappear:(BOOL) animated{
[super viewWillDisappear:animated];
if ([self isMovingFromParentViewController]){
self.countryTextField.delegate = nil;
self.stateTextField.delegate = nil;
}
}
Background:
All my OpenTok methods are in one ViewController that gets pushed into view, like a typical Master/detail VC relationship. The detailVC connects you to a different room depending on your selection. When I press the back button to pop the view away, I get a crash (maybe 1 out of 7 times):
[OTMessenger setRumorPingForeground] message sent to deallocated instance xxxxx
or
[OTSession setSessionConnectionStatus:]: message sent to deallocated instance 0x1e1ee440
I put my unpublish/disconnect methods in viewDidDisappear:
-(void)viewDidDisappear:(BOOL)animated{
//dispatch_async(self.opentokQueue, ^{
[self.session removeObserver:self forKeyPath:#"connectionCount"];
if(self.subscriber){
[self.subscriber close];
self.subscriber = nil;
}
if (self.publisher) {
[self doUnpublish];
}
if (self.session) {
[self.session disconnect];
self.session = nil;
}
//});
[self doCloseRoomId:self.room.roomId position:self.room.position];
}
Here is a trace:
Here is the DetailViewController on Github: link here
How to reproduce:
Make a selection from the MasterVC, that takes you into the DetailVC which immediately attempts to connect to a session and publish
Go back to previous, MasterVC quickly, usually before the session has had an a chance to publish a stream
Try this several times and eventually it will crash.
If I slow down and allow the publisher a chance to connect and publish, it is less likely to cause a crash.
Expected result:
It should just disconnect from the session/unpublish and start a new session as I go back and forth between the Master/DetailVC's.
Other:
What is your device and OS version?
iOS 6
What type of connectivity were you on?
wifi
Zombies Enabled?
Yes
ARC Enabled?
Yes
Delegates set to nil?
Yes, as far as I know
Any help solving this crash would be greatly appreciated. Perhaps I'm missing something basic that I just can't see.
What seems to happen is that the OTSession object in the OpenTok library continues to to send messages to objects in that library that have since been deallocated by switching views. The library has a [session disconnect] method that works fine if you give it enough time, but it takes close to 2-3 seconds, and that's a long time to pause an app between views.
This might be a stupid question, but:
Is there anyway to stop all processes initiated by a certain VC?
Closing the session from viewWillDisappear() works if you can determine for sure that the view is going to be popped, not pushed or hidden. Some answers suggest putting this code in dealloc(). Regarding those suggestions, Apple says,
You should try to avoid managing the lifetime of limited resources using dealloc.
So, here is how you can determine for sure that your view will get popped. viewWillDisappear() is called when the view is popped from the stack, or is otherwise pushed somewhere else. This is the easiest way to determine which, and then unpublish/disconnect if it is truly popped. You can test for this with isMovingFromParentViewController. Also, here is where you can remove specific observers.
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated]
// This is true if the view controller is popped
if ([self isMovingFromParentViewController])
{
NSLog(#"View controller was popped");
// Remove observer
[[NSNotificationCenter defaultCenter] removeObserver:self.session];
...
//dispatch_async(self.opentokQueue, ^{
if(self.subscriber){
[self.subscriber close];
self.subscriber = nil;
}
if (self.publisher) {
[self doUnpublish];
}
if (self.session) {
[self.session disconnect];
self.session = nil;
}
//});
[self doCloseRoomId:self.room.roomId position:self.room.position];
}
else
{
NSLog(#"New view controller was pushed");
}
}
Ref: Testing for Specific Kinds of View Transitions
Looks like OpenTok have a bug with usage NSNotificationCenter inside of OTSession and OTMessenger classes. You can see these classes in call-stack are separated with NSNotificationCenter calls:
You can manually unsubscribe your OTSession object when dealloc (hope OpenTok uses defaultCenter):
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self.session];
}
You need to check if this code (dealloc) is really executed. If not - you need to fix problem of UIViewController deallocation. A lot of other answers contains tips how to help UIViewController to be deallocated.
-(void)viewDidDisappear:(BOOL)animated is called whenever the view is hidden, not only when it is popped from the view stack.
So if you push a view over it, viewWillDisappear will be called and your objects deleted.
This is specially problematic if you load these same objects from viewDidLoad: instead of viewDidAppear:.
Perhaps you should put your unpublish/disconnect code in -(void)dealloc.
This is what Apple suggests:
-(void) dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
But this is only the last resort to remove observers, still often a good habit to always add it to make sure everything is cleand up on dealloc to prevent crashes.
It's still a good idea to remove the observer as soon as the object is no longer ready (or required) to receive notifications.
I most of the time put such a code in the viewWillDisappear, but I guess that doesn't really matter.
I believe the issue is that your session delegate is not set to nil. Just add the following in your viewDidDisappear:
self.session.delegate=nil;
You must call [super viewDidDisappear:animate]; at the beginning. May be it will fix your issue.
And better cleanup your session and subscriber in dealloc method:
- (void) dealloc {
[self.session removeObserver:self forKeyPath:#"connectionCount"];
if(self.subscriber){
[self.subscriber close];
self.subscriber = nil;
}
if (self.publisher) {
[self doUnpublish];
}
if (self.session) {
[self.session disconnect];
self.session = nil;
}
[self doCloseRoomId:self.room.roomId position:self.room.position];
//[super dealloc]; //for non-ARC
}
According to the stack trace you have posted, the notification center reaches out to an OTSession instance that is still alive. Afterwards, this instance provokes a crash calling methods on deallocated objects.
Adding to that the two different deallocated instance messages, we know there are asynchronous events occuring after the death of some objects that trigger the random crash you are having.
As ggfela suggested, you should make sure to nil out the delegates you have connected to the OpenTok framework. I strongly suggest you do that in the dealloc method as we want to make sure that after that point, no one has any dangling references to your object :
- (oneway void)dealloc
{
self.session.delegate = nil;
self.publisher.delegate = nil;
self.subscriber.delegate = nil;
}
Another odd thing in the code is that your handler for sessionDidConnect: creates a new dispatch_queue every time it is being called in order to call doPublish:. This means that you have concurrent threads sharing the SROpenTokVideoHandler instance which makes it prone to race conditions.