We have a complex app that uses a UIWebView to display some information at a certain point. We have an OpenGL layer that stops drawing while the UIWebview is displayed, and we're using about 128 Meg of ram (according to monitors). The page is about 10 screens worth of table, and is static content. Javascript is used on setup, but that's it.
We've noticed that the page loads and is responsive, and then after scrolling the view up and down a few times, the CPU hits about 18% - not in our code. This happens on DidReceiveMemory warning (though the device is not short of memory), so it feels like a garbage collector or something. The 18% then remains until the view is freed off. Returning to the view again and the newly created UIWebview will be responsive again, with the same behaviour of slowing down after a bit of brisk scrolling.
We clear the NSURLCache on didReceiveMemoryWarning (and give the OS back lots of ram). Does anyone have other suggestions, as the responsiveness drops off a cliff on older devices?
It turns out if you present the Renderbuffer from the OpenGL layer every frame while a UIWebView is visible, this slow down kicks in. The other instance we used a UIWebview we had paused the renderer, so it didn't matter
Ensuring that
glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
[m_pContext presentRenderbuffer:GL_RENDERBUFFER];
Does not run (or pausing the display link) fixed the problem for us. Hope it is useful to someone, as it has been puzzling us for days.
Related
My company uses a UIWebView to display ads. The issue I'm having is that initializing a UIWebView appears to be expensive; profiling with Time Profiler shows [UIWebView alloc] initWithFrame:CGRectMake(0,0,500,500)] to take 31–40ms. This is enough to cause noticeable frame drops in games running at 60 FPS.
Is there a way to workaround this slow initialization time? My current ideas are to create a UIWebView when the app launches (but before gameplay has started), and reuse that (potentially creating a pool of them to reuse, like how UITableViewCell works) or to try and see if WKWebView has better performance.
Here are my findings:
WKWebView does not initialize any faster. Creating WKWebViews took a similar amount of time as creating UIWebViews (in the 1 test I did, it took 46ms to create two WKWebViews.
The very first web view created takes significantly longer to create than subsequent web views. The first one takes 31–42 ms; subsequent ones take ~11ms to create. The good news here is that creating the first web view when e.g. the app launches allows future webviews to be created more cheaply, avoiding a 40ms hit while the game is running.
Creating a pool of UIWebViews was a good solution for my use case. By creating the webviews at app launch and then reusing them, I avoid causing a frame drop while the game is running.
There is not much difference in the responsiveness between UIWebView and WKWebView because WKWebView has been introduced for a consistency matter between iOS and OSX. The underlying engine is Webkit in either way and it requires a lot of initialization.
The best solution I've found recent during years has been fading a WebView starting from a view with 0.1 alpha in which the url was loaded. Be careful to not start from 0.0 and do not have your webview detached from main view hierarchy because your url would not be loaded.
When didFinishLoading is called then you can fade it to 1.0 thus providing a better user experience.
Personally I don't like the UIWebView pool because I have experienced some memory troubles when maintaining it, especially on iOS 7 devices.
I have this iPhone app created in Cocos2d 2.1.
WHen the app first starts it presents a full screen background and a UIACtionSheet where the user has to choose one option to start the application. That's it. This actionsheet is presented by the first scene to run by the delegate (HelloWorldLayer scene).
Nothing else is loaded. Nothing is running on any thread that I am responsible for that. Even so, the CPU usage is about 20%. I have profiled that on instruments and this is what I have:
I see this [CCGLView swapBuffers] that is inside something that calls attention, ths CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION (CoreFoundation).
If I choose "show obj-c only" I see this:
Apparently the guilty is this drawScene but how can a scene with a background image and a UIActionsheet use that much CPU?
How do I trace the source of this problem?
NOTE. This is being tested on the device. On simulator CPU usage is 77%!!!!!
Cocos2d, like any OpenGL application, redraws the contents of the screen every frame. Typically that means 60 frames per second. Swapping the frame buffers and all the other overhead associated takes some CPU time.
So what you're observing is normal behavior, there's nothing that can be done about it. There is no concept of "idle" in a rendering engine, it draws the contents of the screen even if they didn't change (or the screen is empty) on the assumption that screen contents usually do change.
You can however pause the CCDirector to reduce framerate, conserving CPU cycles and battery. Or call stopAnimation to completely stop all updates, including scheduled selectors. However what happens to the framebuffer is undefined, usually it sticks around (ie the last rendered frame "freezes") but this may not be true for all situations and devices.
As far as Simulator is concerned: ignore it. Its performance characteristics are not the least bit comparable nor instructive.
I don't have any hardcore data supporting this, but generally, you'll notice that any app you play with or one you're working on, when the app first loads, doing something such as displaying a modal view controller is slow the first time, but every subsequent time, it's really fast. For my app, when I present a modal view controller the first time, it takes about 3 seconds, but if I do it over and over again after that, it takes less than a second every time. What is the reason for this?
There's a bunch of possible explanations here.
Something's been cached. The first time, it had to load something from "disk," the second time it was already in memory. This could be an entire framework, or NIBs or graphic resources in the OS itself.
Memory management. iOS didn't have enough memory to satisfy the request the first time. iOS spent some time clearing out memory, possibly quitting background applications. The second time, it's already available.
Probably many others.
Caching. Off the top of my head, images are often cached, and I wouldn't be surprised if the nib was cached as well.
No, I don't notice this in my app. The cause of what you are seeing could be a hundred different things, so we need a bit more data to make an informed answer.
Suggest you run Instruments, narrow down the time window to the initial 3-second pause, then see what the machine is doing during that time. Run it multiple times and look at CPU, IO, memory, anything that could be slowing it down.
I am writing an iPad app which uses an AVPlayer to display a video. There's buttons to jump to various parts of the video, and when the user rotates the device, I change the size of the view which holds the AVPlayer layer.
My problem is that after a certain amount of device orientation changes and jumps around the video, the app crashes.
I have NSZombie enabled - this doesn't break.
I have a breakpoint enabled in my code to catch exceptions - this doesn't break.
I have run instruments and the code isn't leaking.
Allocations simply shows the "Overall Bytes" growing and growing with every action until it hits 14 meg and the pad crashes.
I feel like I have no way of getting to the bottom of this. Am I missing some trick to solving this? Does AVPlayer need some special treatment when being released?
ANY HELP, MUCH APPRECIATED.
Use instruments to check your Allocations. I recently had a very similar problem where there were no memory leaks but my Overall Bytes kept growing every time I launched a particular ViewController (and it would eventually crash).
It turned out that the ViewController itself was a strong reference as a delegate to another class (oops) and each time I dismissed the ViewController that other class still had a reference to it. Therefore each time I launched and dismissed this ViewController I would create another instance of it that would never die (and never leak).
Your exact problem may be different but you should be able to see the reason for your Overall Bytes growing by checking out your Allocations.
I'm looking for help with a very specific memory-management issue where didReceiveMemoryWarning doesn't appear to be getting called in cases where it should be.
I have a straightforward app that's a story with pages. I have an outer view/controller that manages the page views/controller. Each page view has a picture on it of decent size (200-300k). It's large because it's a universal app, so they're all 1024x768, then get scaled down for the iPhone. I have implemented didReceiveMemoryWarning to release unused controllers (whatever's not showing at the time). The app works fine when didReceiveMemoryWarning gets called, but it does not always get called. On the iPod Touch 2G, if I'm going from page to page fast, it will often just kill the program without calling didReceiveMemoryWarning (I put a breakpoint there to see). On an iPhone 1G which has the same amount of RAM, didReceiveMemoryWarning gets called at reasonable times and I never run out of memory.
The log prints "Received memory warning level 1/2" as expected right before my code does get called, but I don't see it in the logs in the iPod Touch 2G when my app gets killed without a chance to free up memory.
I've used static analysis and the leaks tool and the memory profile looks good. I don't think leaks have anything to do with the problem. Rather, the problem is that my program doesn't get the opportunity to free up resources when memory is tight. I do want to keep unseen pages in memory when there's enough memory - it allows for quick paging and makes the pan gesture for changing pages work responsively.
Has anyone else seen this? If anyone has hints, I'd appreciate it. I'm also curious if anyone knows under what conditions didReceiveMemoryWarning should get called. Is it possible that my program is gobbling up so much memory so fast that iOS doesn't have an opportunity to free up memory?
Memory warnings appear to come too late when allocating a lot of memory "too" quickly, especially if the app doesn't spend enough idle time in the run loop between allocations.
Try preflighting (attempt to allocate and then release) memory, and return to the run loop, maybe a half second before you really need the memory.