memory not releasing with ARC and storyboard in iOS 5.1 - iphone

i'm cracking my head on memory issues with my app, the app are working fine except that it will crash once it hit low memory warning and are very very very laggy when using it for 10 to 20 minutes.
EDIT: how to poptoviewcontroller?
introvideo-> welcomeview & tutorialview-> mainviewcontroller-> scannerviewcontoller-> questionview ->(if answer correct -> correctView) else ->wrongView
how do i pop back to mainView controller ?
the below code are to solve adding view controller to the navigationcontroller.viewcontroller stack. As i'm using storyboard pushing from viewcontroller to another view contoller with out poping.
the code will pop to the viewcontroller that are already in the viewcontroller stack.
the flow of the of my storyboard as attached:
http://dl.dropbox.com/u/418769/storyboard%20flow.png
intro video -> welcome view & tutorial view (if username !exist) -> main view controller
this is the main file that user will alway go to.
http://dl.dropbox.com/u/418769/scannerViewController.h
http://dl.dropbox.com/u/418769/scannerViewController.m
i'm using a custom segue to pop viewcontrollers, which solved part of the problem.
-(void)perform {
UIViewController *sourceVC = (UIViewController *) self.sourceViewController;
NSInteger index = -1;
NSArray* arr = [[NSArray alloc] initWithArray:sourceVC.navigationController.viewControllers];
for(int i=0 ; i<[arr count] ; i++)
{
if([[arr objectAtIndex:i] isKindOfClass:NSClassFromString(#"mainViewController")])
{
index = i;
}
}
[UIView transitionWithView:sourceVC.navigationController.view duration:0.5
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
[sourceVC.navigationController popToViewController:[arr objectAtIndex:index] animated:NO];
}
completion:^(BOOL completed)
{
}
];
}
however, the app are still eating up the RAM and VRAM.
I really appreciate any friends here to help solving my question, does Strong value caused this problem ?

When you say that you're using a "custom segue to pop to the Main View Controller", I'm not sure if I quite understand that. Are you using performSegueWithIdentifier? If so, then you're not popping; you're pushing another copy of the main view controller!
In most storyboards, you don't see a segue out of the right side child view looping back to the left of the parent view (and your screen snapshot showed a dizzying number of segue's entering back into the main view controller, which is a bit of a red flag). This is a far more customary storyboard (taken from Beginning Storyboards in iOS 5) at Ray Wenderlich):
(source: cloudfront.net)
Usually you'll dismiss a child view with something like the follow, not use a segue.
[self.navigationController popViewControllerAnimated:YES];
Of, if you wanted to pop back multiple levels, you would use popToViewController or popToRootViewControllerAnimated. Or if you use modal segues, you would dismiss the modal with dismissViewControllerAnimated.
If I've misunderstood what you mean't by "custom segue to pop", can you provide what code you're using do to that?

Computer-aided analysis is the way to solve this. Do Build and Analyze and resolve all issues. Run your app under the Leaks and Allocations instruments. Use heap-shot analysis.

#ken-thomases analysis is spot on. Also see Finding leaks.
I presume you are using ARC?
Can you explain what is the purpose of the above code sample and what your app is doing to require something like this? It feels like you're addressing a symptom rather than solving a problem.
In answer to your question, the use of strong is unlikely to be the source of problems, unless you have strong reference cycles (see Transitioning to ARC). If you follow Ken's suggestions, you'll identify where your app is leaking memory (assuming it is), and if so, where it is. At that point, if you still don't understand the source of the leak, you can post the offending code here and I'm sure there will be plenty of people willing to help. Also, if you have some code that you wonder whether the strong reference is inappropriate, post that relevant code and, again, I'm sure we'll be happy to help if we can.

Related

objective-c: help me understand views and modal transition

I'm lacking a serious understanding of how modal transitions work, so please do not leave the simplest answer out of this.
I have two views with their own view controllers set up in storyboard. A button on the main-menu leads to other-view. I set up this transition purely via ctrl-click and selecting the modal transition. I also have a button leading from the other-view back to the main-menu, set up similarly.
To further my understanding of these transitions I decided that I want the main menu to play a sound when it loads up, but only the first time i hit run, and not again when i go hit the button the other-view to go back to menu-view.
in my menu-view I have a private property BOOL
#interface MainMenuViewController ()
#property BOOL menuSoundPlayed;
#end
and my viewDidLoad...
- (void)viewDidLoad
{
[super viewDidLoad];
if (!self.menuSoundPlayed){
//sound setup code omitted for clarity
AudioServicesPlaySystemSound(mySound);
self.menuSoundPlayed = YES;
}
}
can someone help me understand why the menu sound plays every time main-menu view loads? I do acknowledge that the menuSoundPlayed is never really initialized, but I dont know where I would even do that.
side-note: I love that apple gives us all these conveniences like story-board, but I almost wish it was easier to see all the code behind these things, so i could actually understand what was going on when i 'segue' between views.
Anyways, thank you!
I did a bit more researching and answered my own question.
In the situation I had before (described in my question) I had the code
[self performSegueWithIdentifier:#"mainMenuSegue" sender:sender];
being executed, which as commenters described, starts a new instance of my main-menu.
What i wanted was to return to the main-menu instance already created.
To do this the following code is necessary (with your own completion block obviously):
[self dismissViewControllerAnimated:YES
completion:^{
NSLog("view dismissed");
}];

iOS 5 issues: Navigation bar clipped after dismissing modal

I have a nice little app on the app store that does pretty well for itself. Life was great until iOS 5 came to town. Now, I have a number of issues with my app that I have no way of fixing because I have no clue what is going on, because I feel that they are iOS 5 issues, not mine.
Was there an iOS 5 conversion manual I missed? Or did they just change everything for fun, and want us to figure out where all the easter eggs were?
Here is another issue I am experiencing (that I have wasted so much time trying to fix), that DON'T EXIST AT ALL when I simply say that I want to run the app in good ol' 4.2:
Modal view
My app is a simple reader app. I have a book reading view that displays text with a UIWebView. One of the features I have been working on involves the ability to take notes as you read. This is achieved by hitting a button, and presenting a modal view. Yes, a modal view. The most simple pre- iOS 5 thing you could possibly do. Now, when I dismiss my modal view, just by hitting cancel, and simply dismiss the view, when I get back to my reader view, the navigation bar at the top is pushed up half way off the screen! This doesn't happen in 4.2, but there it is in iOS 5!
What can I do to get this issue resolved?
Thanks for your help.
Ok, I was just able to figure out what in the blazes was going on. I had the shouldAutorotateToInterfaceOrientation value set to a BOOL variable, so that when the modalView was coming back, it didn't know the state/size of the status bar. Fixed that, and the problem disappeared.
I have the feeling it has something to do with the way you present and dismissing the modalview. Apple introduced a new method to present views. May you try using theses instead of the old ones and see if it fixes your problem.
So here is what you do:
change this method:
presentModalViewController:animated:
into the new preferred method introduced with iOS 5:
presentViewController:animated:completion:
Depending if you are using dismissModalViewControllerAnimated:to dismiss your view, change it into dismissViewControllerAnimated:completion.
This methods also have completion handler which is very useful to do some extra work after the view has been presented/dismissed. Maybe that also helps with your other issue. Let me know if that might helped.
A major change in iOS 5 is that the navigationController property of UIViewController is no longer set for modal views. Instead, there is a new (not present in iOS 4) parentViewController property. So where you're using navigationController in a modal view you need to change the logic to something like:
UIViewController* parent;
if ([self respondsToSelector:#selector(parentViewController)]) {
parent = self.parentViewController;
}
else {
parent = self.navigationController;
}
(That's from memory, so I can't guarantee that every t is dotted and every i crossed.)
I was seeing this same clipping problem.
I found out that the reason for my issue was that I set the content size within the modal dialog (something I did for my iPad layout), so removing these two lines seemed to fix the issue:
CGSize size = CGSizeMake(320, 480);
self.contentSizeForViewInPopover = size;
I thought the problem was fixed but it wasn't. After reviewing the code some more, cleaning the build, and retesting it turned out to be a shouldAutorotateToInterfaceOrientation which would return NO for all orientations, for a brief amount of time (flag == NO) while the app is loading (root controller). You want to at least return YES to one orientation like so:
- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return !self.flag ? UIInterfaceOrientationPortrait == toInterfaceOrientation : YES;
}

iOS5 - manual Storyboard view selection in code

I am new to iOS and using storyboards for the first time. When my app starts it checks back with the a server app I have written to see if the saved credentials are authenticated and I then in my AppDelegate class I then attempt to show the appropriate scene in the app's storyboard - MainMenu if authenticated or a Login Screen if not authenticated.
I have tried using instantiateViewControllerWithIdentifier on the storyboard and also the performSegueWithIdentifier on the initial NavigationController which is set to be the "Initial View Controller" to display the appropriate view..
However with both methods only the blank navigation bar shows and I am unsure where to go from here.
If there was some example code on how others manually manipulate storyboard scenes and viewcontrollers that would be great. Am I maybe putting the code in the wrong place (ie should it go into the first View Controller) or should that not matter? No exceptions are raised and I seem to have access to instantiated objects as required.
I am thinking I need to understand the operation of the app delegate's window more, or maybe should I focus on manually loading the storyboard by removing it's reference from the InfoPlist settings?
Any thoughts would be greatly appreciated.
From my (admittedly haphazard) understanding of storyboards (at the moment), you should have two named segues going from a first viewcontroller, and then you can simply trigger one or the other as need be (I presume there's some sort of "loading/authenticating" screen, however brief?)
if (success) {
[self performSegueWithIdentifier: #"MainMenuSegue" sender: self];
} else {
[self performSegueWithIdentifier: #"LoginSegue" sender: self];
}
To debug, I'd set up buttons on the initial viewcontroller just to be sure the segue linkings/etc are proper.
You really shouldn't need to instantiateViewControllerWithIdentifier unless you're working around segue/storyboard limitations. I think.
I've put the performSegueWithIdentifier in my app's first viewcontroller's viewDidAppear (not the best idea, I think; but that's sort of the soonest it should happen? and I would hedge towards saying it should be triggered somewhere in the viewcontroller stack, not from the appdelegate, but I haven't tested that).

How to release a view when it's not visible in a tabbar controller?

I have a simple app with a tabbar navigation interface.
It has three view (A, B and C) and a modal view. Each view has its own view controller and nib . They are all designed and connected in the interface builder.
I'd like to release views which are not visible.
Tried release and nil them when another view appears such as
[[[self.navigationController.viewControllers objectAtIndex:0] view] release];
[[self.navigationController.viewControllers objectAtIndex:0] view] = nil;
etc.
It doesn't cause any issues but when I run instruments it does not make any difference. I don't see any drop in memory usage
I would appreciate your help
as #Daryl Teo wrote you should release and recreate in viewWillDis/Appear and (thats why I write this answer) you have a method called didReceiveMemoryWarning, use it!
You can simply log out whenever it gets called and test it with the Simulator included memory warning test function.
Simply open a tab, open another tab and call that test function. Your debug console should print out the log. If not you should double check if you have released all objects maybe someone is over-retained (which again should be released in viewWillDisappear).
The drop in memory usage might not be significant, depending on what the released viewController holds on to. I sugest you out a NSLog into the 'dealloc' of the viewController to see if it really gets dealloced or if there is some other object still holding on to it. Remember that release won't free the memory, it will only do so (by calling dealloc) if the objects retain count reached 0.
You don't want to do this. Let the TabBarController handle your view controllers for you. (It will already retain your viewController internally so whatever you do will only make retain count go out of sync)
You may be able to make it more memory efficient if you release objects in viewWillDisappear. Then rebuild the data again in viewWillAppear.

How to actually implement the paging UIScrollView from Apple's example?

I found an example of a paging UIScrollView in Apple's developer docs and it's just what I want for my application. I have a main view with a "Help" button that I want to present a view that users can page through to see all the help topics. The sample is at:
https://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/UIScrollView_pg/ScrollViewPagingMode/ScrollViewPagingMode.html%23//apple_ref/doc/uid/TP40008179-CH3-SW1
And it works as advertised, but, as with so many of Apple's examples, they do all kinds of code right in the AppDelegate. I have my own AppDelegate, but then I have a NavigationController with a RootView that has this "Help" button on it. I can't seem to get the example code integrated into my own and have it work. I'm pretty sure I can't put the code they have in their AppDelegate in my own, but how to set it up eludes me.
Can someone point me to an example where they do what I'm talking about?
EDIT: I was able to create a new project and get it to work like Apple's by moving all the AppDelegate methods into the UIViewController that the template supplied and creating a new ContentPage UIViewController to hold the content. It can scroll from page to page, so I think I can insert this code into my other project ok.
I replaced the applicationDidFinishLaunching with an equivalent viewDidLoad and got rid of the AppDelegate stuff dealing with window and such. Then I changed Apple's initWithPageNumber: method to refer to my help pages rather than just creating instances of their generic views.
- (id)initWithPageNumber:(int)page {
NSLog(#"--initWithPageNumber:ContentPage");
if (self = [super initWithNibName:[NSString stringWithFormat:#"HelpPage%d", page] bundle:nil]) {
pageNumber = page;
}
return self;
}
Thanks for the help - sometimes it's good just to have someone tell you it can be done to keep going!
In the sample code, the applicationDidFinishLaunching: method sets up the UIScrollView (and a UIPageControl, but let's ignore that for now) and then "loads" the first two pages. "Loading" a page consists of loading that page's view controller into memory and caching it. It then adds that controller's view as a subview to the UIScrollView offset with an appropriate x based on what "page number" it is. You should be able to do all that in your viewDidLoad.
If you have a blank view when your view controller's view is shown, then you haven't added the subview correctly to your UIScrollView. Depending on what exactly you changed, that could be some combination of (a) not getting the x offset correct, (b) not setting the frame correctly, or (c) not adding it as a subview correctly.
If you post some of your own code we might be able to shed some light on the problem.