I know the title is not that explanatory but here is the story: I am developing a browser game, mostly using JavaScript and the Mapbox library.
Everything works well on desktop, Android and iOS but one problem appears on iOS: after letting the game run for a few minutes the phone suddenly begins to have graphic artifacts and display most of the text scrambled.
Here are some photos of what the phone begins too look like:
My question is: what exactly in my code can cause this? A memory leak? (LE: it turned out to actually be a memory leak)
The real question is: How comes that you can almost brick the entire phone by simply browsing a web page? Shouldn't Safari stop this, or at least the iOS ?
This is not a problem with this specific device, as this problem can be reproduced on different iPhone devices. (I'm not so sure about different iOS versions).
How I can reproduce the error:
iPhone 5C
iOS 9.2.1 (13D15). It also occurs on the new iOS 9.3 version.In order to fix this issue I have to:
Some details about the game itself:
EDIT2: Found the memory leak (as expected). After fixing this leak (check for undefined
_icon) the issue no longer occurs. This means, that somewhere along those lines the Safari/iOS bug is triggered.
Here is what exactly was being called each tick, for each unit that was clustered (was hidden and grouped with others inside a MarkerCluster):
var $icon = $(marker._icon); // marker._icon is undefined because of the clustering $icon.html(''); $icon.append($('<img class="markerIcon" src="' + options.iconUrl + '" />')); var iconX = 10; var iconY = -10; var iconOffset = 0; for(var v in this.icons) { this.icons[v].css('z-index', + $icon.css('z-index') + 1); this.icons[v].css('transform', 'translate3d(' + iconX + 'px,' + (iconY + iconOffset) + 'px,' + '0px)'); iconOffset += 20; this.icons[v].appendTo($icon); } // Fire rate icons this.attackRateCircle = $('<div class="circle"></div>'); this.attackRateCircle.circleProgress({ value: 0, size: 16, fill: { color: "#b5deff" }, emptyFill: 'rgba(0, 0, 0, 0.5)', startAngle: -Math.PI / 2, thickness: 4, animation: false, }); this.attackRateCircle.hide(); // Create and display the healthbar this.healthBar = $('<div>').addClass('healthBar '); this.healthBar.css('z-index', $icon.css('z-index')); this.healthBarFill = $('<span class="fill">'); this.healthBar.append(this.healthBarFill); $icon.append(this.healthBar); $icon.append(this.attackRateCircle);
And this is the icons
array:
this.icons = { attack_order: $('<img src="img/attack.png" class="status_icon">'), attack: $('<img src="img/damage.png" class="status_icon icon_damage">'), hit: $('<img src="img/hit.png" class="status_icon icon_hit">'), };
circleProgress
call is from this library: https://github.com/kottenator/jquery-circle-progress
Yay, I have been able to create a jsFiddle that reproduces the bug: https://jsfiddle.net/cte55cz7/14/ Open on Safari on iPhone 5C and wait a couple of minutes. On iPhone 6 and iPad mini the page crashes (as expected due to the memory leak)
Here's the same code in a HasteBin, for anyone who doesn't want to run it.
You can clear website data occasionally to improve Safari performance. Go to Settings > Safari. Tap Clear History and Website Data. Tap Clear History and Data to confirm.
One of the simplest and most common fixes for Safari not loading, or crashing, on an iPhone is turning off Safari Suggestions. While we aren't sure why this works, many users have reported that turning this off fixes the problem. To turn off suggestions, head to Settings > Safari > Safari Suggestions and turn this off.
Sometimes the Safari problem may be caused by incorrect network settings. Therefore, you can try to fix your Safari issue by resetting all Network settings to default. To reset network settings, just go to Settings > General > Reset > Reset Network Settings.
This memory leaks is probably due to how 'WebKit’s JS Engine' works [safari webkit-javascript llvm]
and really looks like to be a virtual memory buffer-overflow, having a direct impact on the remaining RAM (shared and used also by iOS to store User Interface graphical elements)
Relatively to the piece of code: "[...]finding jQuery memory leaks is easy. Check the size of $.cache. If it’s too large, inspect it and see which entries stay and why. [...]" (http://javascript.info/tutorial/memory-leaks)
Let me expect that it is relative to this for loop :
for(var v in this.icons) { this.icons[v].css('z-index', + $icon.css('z-index') + 1); this.icons[v].css('transform', 'translate3d(' + iconX + 'px,' + (iconY + iconOffset) + 'px,' + '0px)'); iconOffset += 20; this.icons[v].appendTo($icon); }
Assuming inspection is done, and also assuming the fact that you find the entries, you may want to clean the data manually with removeData() or you may use first $elem.detach() and then put $(elem).remove() in setTimeout.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With