Losing Reference of currently opened View - ios

When Push Notifications has been received in app delegate did receive, what i am doing is taking the last object from navigation stack and calling one of the function of that class.
if([[self.navigationController.viewControllers lastObject] isKindOfClass:[JFFriendsListViewController class]]){
JFFriendsListViewController *friendlist=[self.navigationController.viewControllers lastObject];
[friendlist RefreshRequiredOnSameView];
}
Please help How to get the scrollview from xib??
And When i touch any view... It again regain the Iboutlets references from xib.

Run your app and go to the JFFriendsListViewController page and add a debugger inside viewDidLoad and check the memory address of this class.
Again when you receive push notes then here
JFFriendsListViewController *friendlist=[self.navigationController.viewControllers lastObject];
again check the memory address of friendlist.
Are they same???
I think wen you are receiving push notes you are using diff JFFriendsListViewController instance and your subviews are not loading in main thread yet.

Related

Over-released object iOS app crash

My app is crashing with the last message in the device console:
objc[5105]: Cannot form weak reference to instance (0x10801ec00) of class UIPageViewController. It is possible that this object was over-released, or is in the process of deallocation.
Randomly app also crashed with the same error message but UINavigationController instead of UIPageViewController. A bug is reproducible on the simulator and physical devices (iOS 11.2.5).
Profiling with "Zombie" template in Instruments did not give any valuable information. I found that outdated guide useful:
http://www.corbinstreehouse.com/blog/2007/10/instruments-on-leopard-how-to-debug-those-random-crashes-in-your-cocoa-app/
My answer might not fix your issue but I hope it will help to track it. Additionally, I'll appreciate if someone explains why ARC fired on a different thread.
By subclassing UIPageViewController and setting breakpoint in it's dealloc I found that it's not executed on the main thread.
don't do this in production - only play around when debugging
Synchronously removing its view from view hierarchy on the main thread did fix the issue. I have no view controller container responsible for that:
- (void)dealloc {
dispatch_sync(dispatch_get_main_queue(), ^{
[self.view removeFromSuperview];
});
}
However it was not even about the view, while just delaying execution was enough to fix the crash, and the view will be cleaned together with its superview.
- (void)dealloc {
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"%#", self);
});
}
View controllers were stored in the array, and I knew that when this array was destroyed, view controllers are as well. I checked on which thread object holding the only one reference to this array is destroyed, and it is main thread. ARC should release objects on same thread where the last reference to the object was removed, but that is not the case in my app. https://stackoverflow.com/a/32800112
How did I fix my issue:
I don't why it was so. I did not search for a reason anymore when I found that issue will also disappear when I manually remove reference to the array, right before removing the last reference to its owner.

How to remove SFSafariViewController as a child view controller correctly?

I am using the technique provided by this SO answer to preload some URL in SFSafariViewController like this:
addChildViewController(svc)
svc.didMoveToParentViewController(self)
view.addSubview(svc.view)
And I try to remove the Safari View controller with following code:
svc.willMoveToParentViewController(nil)
svc.view.removeFromSuperview()
svc.removeFromParentViewController()
Now I can preload the URL and show the Safari View without problem. However, after I repeat the process (preload/show/remove) several times (probably 30+ times) , the app will crash due to some memory issue because the log shows Memory level is not normal or this app was killed by jetsam when the app crashes.
Before crash, I saw some logs about possible-leak warnings:
<Warning>: notify name "UIKeyboardSpringBoardKeyboardShow" has been registered 20 times - this may be a leak
<Warning>: notify name "com.apple.SafariViewService-com.apple.uikit.viewService.connectionRequest" has been registered 20 times - this may be a leak
Am I doing it correctly when removing the Safari View controller? Am I missing something? Or any suggestion to work around this issue?
If your adding child view controller code is as you have specified above then I think its order should be a bit different as per the documentation.
addChildViewController(svc)
view.addSubview(svc.view)
svc.didMoveToParentViewController(self)
You should first add the child view and then call didMoveToParentViewController. Try this and see if it works.
Listing 5-1Adding a child view controller to a container
(void) displayContentController: (UIViewController*) content { [self addChildViewController:content]; content.view.frame = [self
frameForContentController]; [self.view
addSubview:self.currentClientView]; [content
didMoveToParentViewController:self]; }
In the preceding example, notice that you call only the
didMoveToParentViewController: method of the child. That is because
the addChildViewController: method calls the child’s
willMoveToParentViewController: method for you. The reason that you
must call the didMoveToParentViewController: method yourself is that
the method cannot be called until after you embed the child’s view
into your container’s view hierarchy.
you are probably leaking svc. nil it out after removing it.
svc.willMoveToParentViewController(nil)
svc.view.removeFromSuperview()
svc.removeFromParentViewController()
svc = nil
if this doesn't solve it, try enabling zombies or use the leaks instrument

Dealloc called without viewDidLoad being called (crash on removing KVO observer)

I am using a UITabBarController, and my 3rd tab observes an array on a singleton data store (implemented in viewDidLoad).
Currently if I just log out (and change root view controller from App Delegate), the app will crash when dealloc is called on that 3rd tab with the message "cannot remove observer for the key path "X" because it is not registered as an observer.
Using breakpoints, I see that viewDidLoad is never called on this 3rd tab, however dealloc is being called when I sign out. What is going on? I assume the UITabBarController is holding a reference to the 3rd tab when I enter the storyboard, but does not "load" that tab. Yet iOS calls dealloc on it when I release the tab bar controller.
Should I use a boolean to track viewDidLoad execution, or try to remove the observer with a #try statement? Is there an overall better design for this?
Do not use #try. Exceptions in Objective-C should always be considered programmer error, and should be fatal.
As you say, use a boolean ivar set in -viewDidLoad to avoid this.
The view has not been loaded because views are only loaded when they are required for display.
Raw KVO can be dangerous and unwieldy. While not required to answer this question, ReactiveCocoa significantly improves the KVO experience.
viewDidLoad is called before the view appears for the first time. UITabBarController is creating the relevant UIViewController, but the view is not loaded during creation. It is loaded on-demand, when a user visits the tab for the first time.
KVO removal is problematic, I don't think you can avoid using #try in dealloc. I would suggest to use KVOController: it's fairly easy to use and it would also handle all the edge cases for you.
May have found an even better solution. I add the observer in the method initWithCoder:(NSCoder *)aDecoder, which is called when the parent UITabController is loaded. I am using the storyboard which may be why I need to call override this method instead of regular init. Doing this now without the need for a BOOL flag or #try and no crashing.
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[anObject addObserver:self forKeyPath:aKeyPath options:0 context:NULL];
}
return self;
}
Use a flag to set whether or not KVO has been set up. Using #try can create memory management issues depending on the state of the app.

Message was sent to a deallocated object (ARC)

In my app I've a UITableView in which I have custom UITableViewCells and those cells contain UIWebView in which I am displaying YouTube video using <iframe>. When I click on a video and instantly scroll the table before a full screen player opens, the app crashes. I tried Instruments to find the problem and came to know that An Objective-C message was sent to a deallocated 'AAHomeNewsListCell' object (zombie) at address: 0x118e09800.

I'm using ARC and I can't find a way to resolve this issue.
You are sending a message to an object that is no longer alive. The best way to debug that problem would probably be to set a break point as close as you can to where the situation happens, and step through the code. A good place to start would be close to where you use an AAHomeNewsListCell-object, or close to where you trigger the full screen player.
If I had to guess, I'd say that the web view is messaging its delegate but the delegate has already been deallocated. UIWebViewDelegate comes with this handy warning:
Important: Before releasing an instance of UIWebView for which you have set a delegate, you must first set the UIWebView delegate property to nil before disposing of the UIWebView instance. This can be done, for example, in the dealloc method where you dispose of the UIWebView.
Looking at your zombie message, what is it that would be still trying to message the cell? Is your cell acting as the web view's delegate?
Edit Looking at what message is being sent, it gets _setFirstResponder:. It could be a text entry form in the web view that's trying to get first responder to enter text.

Receive memory warning and memory leak

I'm using ARC (automatic reference counting).
Is it ok if I set the IBOutlets to nil in the viewDidDisappear instead of viewDidUnload?
Such as these:
[self setTheImage:nil];
[self setBTNplay:nil];
[self setBTNstop:nil];
I'm writing a navigation based app which includes pageViewController, I tested my app in Instruments to see the memory leaks and I keep getting receive memory warning message.
I've even put a log code in the viewDidUnload method. But it doesn't seem to get called when I even pop to rootViewController!
One more thing: If each page has an audioPlayer, where should I set a #property (nonatomic, strong) AVAudioPlayer *audioPlayer; to nil?
Or how do I set it to weak instead of strong? Because it gives me a 'warning' then in this code line:
_audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:poemURL error:nil];
it says: Assigning retained object to weak variable
You don't need to nil out those values in viewDidUnload. Make sure you're using weak properties instead of strong or assign for IBOutlets. Received memory warning doesn't necessarily mean you're leaking. Received memory warning means that your app is consuming too much memory. Run Instruments and edit your question with how much memory your app uses.
The fact that you're using AVAudioPlayer makes me thing that maybe you're pulling down some massive audio files into memory.
Also by the way, initWithContentsOfURL:error: will get you rejected from the App Store because you're blocking the main thread. Try testing your app on an iPhone with only cellular enabled and go into a part of your office/house that has a bad internet connection. Also try with your phone switched to airplane mode. Your app will undoubtedly either freeze for a long time before the connection fails or it will simply crash.
Instead, you should be using grand central dispatch or downloading it via NSURLConnection's block or delegate methods.
First, do not set to nil your properties in viewDidDisappear cause your view is still loaded. You must always set them to nil in viewDidUnload. It's invoked in low memory situations and here you must clean-up all stuff that breaks system memory.
Apple's UIViewController reference for viewDidUnload
When a low-memory condition occurs and the current view controller’s
views are not needed, the system may opt to remove those views from
memory. This method is called after the view controller’s view has
been released and is your chance to perform any final cleanup.
Second , take a look at this tutorial where is explained very well ARC
Are you calling [[NSNotificationCenter defaultCenter] removeObserver:self]; from one of your view controller subclasses? If so, that would explain why you're not getting viewDidUnload called.
If that's the problem, you should remove yourself from specific notifications when needed rather than all notifications as above. (It's OK to call removeObserver:self from dealloc, though.)
Is it ok if I set the IBOutlets to nil in the viewDidDisappear instead
of viewDidUnload?
There are many things wrong from this statement.
First of all, you do not set IBOutlets to nil in viewDidDisappear. viewDidDisappear is called when the view "disappears" (e.g. when it's in a tab bar controller, and you switch to another tab; or it's on a navigation controller, and you push something on top of it); the view can then "appear" again without loading again. If you set IBOutlets to nil, they will not be set again when you appear. (They are only set when the view is loaded.)
Second, if you have a leak, and setting stuff to nil "fixes it", that means you are not releasing the instance variables. You must always release retained instance variables in dealloc.
I've even put a log code in the viewDidUnload method. But it doesn't
seem to get called when I even pop to rootViewController!
Yes, viewDidUnload is only called in low memory situations. It is not called normally in most situations. If you were depending for it to be called, you were using the wrong method.

Resources