I would like to know if I am using good memory management practise with UIWebView. My app makes extensive use of hundreds of local html files, which the user can get to at the end of a table hierarchy. The user can also swipe left and right for previous and next pages.
In viewDidLoad, I set everything up, such as background colour and gesture recognizers, and I add it to the view:
[self.view addSubview: self.myWebView];
Only in dealloc do I set the UIWebView's delegate to nil, and release it.
In viewWillAppear, I set the UIWebView's delegate to self, while in viewWillDisappear, I set the delegate to nil (as well as ask it to "stopLoading").
Each time I load a new document in, I use loadHTMLString:
[self.myWebView loadHTMLString:fullHTML baseURL:nil];
Is what I'm doing sufficient to keep the memory happy? Is this issue from two years ago still relevant?
Thank you!
What you have written should not leak memory. I don't know if the issue you linked to still happens, but I have not had problems with it in my use of UIWebView.
Related
My app has multiple buttons each of which brings up a different UIPopoverController. We have 'passthroughViews' set so the buttons are still enabled while the popovers are up. Pressing one of the buttons while a popover is displayed dismisses the current popover and brings up the new one. The trouble is, this seems very slow on iOS8. The popovers come up and go away on their own just fine -- it's just when we switch from one to another that there is a pause between when the first one closes and next one starts to show. I'm calling these dismissPopoverAnimated and presentPopoverFromRect: calls back to back, so there is nothing going on between the two. I've tried setting 'animated' to NO for both of these but the pause still remains. Any help is greatly appreciated.
Rather than close the popover, re-purpose the same popover when the 2nd button is pressed. Move the location of the popover, on the screen and load the new content into it. Seems like that would be quicker and avoid whatever contention or latency issues you are experiencing switching from one modal view to the next (I suspect that is what the issue is). You'd lose the disappear/reappear animations, but it should be near instantaneous and provide a good user experience, because users don't really (in the long run) want to wait through animations to see their content when they push a new button anyway.
What's inside these popovers? If they're tied to something running on the main thread, then you are likely seeing that as your lag element. You might want to lean up your viewDidLoad and viewWillAppear methods. Try running largish processes on a background thread and updating it after appearing.
If you are using a core data store then you are likely using the primary context, which always runs on the main thread. Try caching those calls ahead of time, or run your fetches on a child thread and returning those to the main thread after the popover has loaded.
Try this code,
[UIView transitionWithView:pop1.contentViewController.view duration:0 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
[pop1 dismissPopoverAnimated:NO];
} completion:^(BOOL finished) {
[pop2 presentPopoverFromRect:btn.bounds inView:self.view permittedArrowDirections:UIPopoverArrowDirectionLeft animated:NO];
}];
hope it helps.
I had a similar issue that had my head banging on the problem for almost a day: the UIPopoverController was extremely slow to appear on occasion (sometimes it was sluggish, other time almost fine, other it was taking like 4" to appear...), and this happened only on iOs8 (iOs7 was always blink-fast), which leads me to believe that my solution might help you as well.
After much debugging I have come to the conclusion that the problem for me was connected the fact that I showed the popover within the
- (NSIndexPath *) tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath;
method (but I would'n rule out that other similar cases exist).
My solution is running the presentPopoverFromRect: method (my logs showed it was the slow method) just after the willSelectRowAtIndexPath: method ended (as a matter of fact I crammed the whole popover "initialize and show" in the function to delay as well, to be on the safe side, and because my code was already like that).
This boils down to running the UIPopoverController initialization and display code in a block like this one:
dispatch_async(dispatch_get_main_queue(), ^() {
// Do the popover stuff here
});
Even if you are not using table, other similar problems might exist within iOs8 (because this is an Apple bug!), so I guess this might worth a try...
Hope it helps,
Cheers
I am developing a little MS tweak that adds a view on the Springboard. I want it to be constantly updated so I called it into the
- (void)showSpringBoardStatusBar
Then I create and add the view using this:
[[UIApplication sharedApplication].keyWindow addSubview:view1];
Is this the right way?
But the problem is this view uses a low alpha level to be transparent and every time the view is updated by the showSpringBoardstatus bar another UIView is added onto of it eventually just making the view solid. This also is uneconomical in terms of memory. so then I went back an added what I thought would remove the code:
view1 = nil;
[view1 removeFromSuperview];
But it seems like this doesn't make a difference as it is still there and nothing changes.
I have been searching for the last few days for anything to help me with this but got nothing out of it. All I can think of is that I can't remove an added subview from a key window like I would from a normal view or I just don't how to do it correctly.
Any help would be appreciated. Thank you.
Setting view1 to nil and than calling "removeFromSuperView" might be a bad idea. How about adding the view only once, keeping a reference and updating this reference constantly without adding it to keyWindow again?
Here is the problem :
I am writing an app which displays some pictures, with a treemap layout (for an example, see https://raw.github.com/beckchr/ithaka-treemap/master/Core-API.png)
This layout is displayed in a UIScrollView. Since many pictures can be added to that scrollview, I want to release the ones which are not on currently on screen. I am not using ARC.
At my point, I know which pictures I should release, and how to do it while scrolling (calling some "unload" method). There is no useless call of that method. The problem is that, when pictures are released, the scrolling stops for a little moment (a few ms, but this is enough to be bad looking, making the scroll kind of "jumping" and slow, not smooth at all).
What I've tried (put in the body of my "unload" method) :
imageview.image = nil
performSelectorInBackground:#selector(effectiveUnload) withObject:nil
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0,^(void){
dispatch_sync(dispatch_get_main_queue(), ^(void){
imageview.image=nil}
}
I think this problem is weird, since there is absolutly no slowing effect with memory allocation, but only with memory release.
Thanks for help, don't hesitate to ask for more information.
did you try removeFromSuperview to remove the imageview from scrollview
Don't add more and more UIImageViews - recycle them!
To save as much memory as possible you should follow the UITableView way of recycling views:
1. Once a view has left the visible area, add it to a "views pool" (and remove it from its superview. It's not an expansive operation!)
2. When a new view becomes visible, first check if there's a view in the pool. If not, then create a new view.
I know my answer doesn't answer your question directly, but that's the way to go. Otherwise you'll run out of memory eventually.
I'm writing an iOS app that has a problem on a view that's about 4 taps deep into the UINavigation stack. It's becoming a pain to have to repeatedly tap tap tap through the simulator to drill down to the UIViewController I need every time I want to run the thing.
Is there a way to automate this?
I tried just instantly calling [self tableView:self.tableView didSelectIndex... manually, however that blows up because data hasn't been loaded into the table yet...
I'd prefer something fast w/o a lot of overhead to it - otherwise it'll take more time to implement the solution than I'd save by not tapping the screen 4 times...
Thanks for any insight you guys can provide.
I do this all the time - in the appDelegate, I just add some code wrapped in a #ifdef that just makes the subview the initial view of the navigation controller. Once you get the subview working you can turn the ifdef off. Using the technique will save you lots of time - in fact I'm using it right now to add functionality to my app in the store.
I have always been a bit unclear on the type of tasks that should be assigned to viewDidLoad vs. viewWillAppear: in a UIViewController subclass.
e.g. I am doing an app where I have a UIViewController subclass hitting a server, getting data, feeding it to a view and then displaying that view. What are the pros and cons of doing this in viewDidLoad vs. viewWillAppear?
viewDidLoad is things you have to do once. viewWillAppear gets called every time the view appears. You should do things that you only have to do once in viewDidLoad - like setting your UILabel texts. However, you may want to modify a specific part of the view every time the user gets to view it, e.g. the iPod application scrolls the lyrics back to the top every time you go to the "Now Playing" view.
However, when you are loading things from a server, you also have to think about latency. If you pack all of your network communication into viewDidLoad or viewWillAppear, they will be executed before the user gets to see the view - possibly resulting a short freeze of your app. It may be good idea to first show the user an unpopulated view with an activity indicator of some sort. When you are done with your networking, which may take a second or two (or may even fail - who knows?), you can populate the view with your data. Good examples on how this could be done can be seen in various twitter clients. For example, when you view the author detail page in Twitterrific, the view only says "Loading..." until the network queries have completed.
It's important to note that using viewDidLoad for positioning is a bit risky and should be avoided since the bounds are not set. this may cause unexpected results (I had a variety of issues...)
This post describes quite well the different methods and what happens in each of them.
currently for one-time init and positioning I'm thinking of using viewDidAppear with a flag, if anyone has any other recommendation please let me know.
Initially used only ViewDidLoad with tableView. On testing with loss of Wifi, by setting device to airplane mode, realized that the table did not refresh with return of Wifi. In fact, there appears to be no way to refresh tableView on the device even by hitting the home button with background mode set to YES in -Info.plist.
My solution:
-(void) viewWillAppear: (BOOL) animated { [self.tableView reloadData];}
Depends, Do you need the data to be loaded each time you open the view? or only once?
Red : They don't require to change every time. Once they are loaded they stay as how they were.
Purple: They need to change over time or after you load each time. You don't want to see the same 3 suggested users to follow, it needs to be reloaded every time you come back to the screen. Their photos may get updated... you don't want to see a photo from 5 years ago...
viewDidLoad: Whatever processing you have that needs to be done once.
viewWilLAppear: Whatever processing that needs to change every time the page is loaded.
Labels, icons, button titles or most dataInputedByDeveloper usually don't change.
Names, photos, links, button status, lists (input Arrays for your tableViews or collectionView) or most dataInputedByUser usually do change.