jlowe jlowe - 1 month ago 21
iOS Question

Memory leak in scanner plugin only in iOS 10

I have a cordova(currently 5.3.3) project that uses the phonegap-plugin-barcodescanner (v 6.0.1). Recently I have encountered what I believe is a large memory leak of some kind in iOS 10. When the application runs on iOS 10, it appears to never deallocate the resources tied to the scanner. However on iOS 7 or 9, everything works fine. This can be seen below in the memory reports.

iOS 10
ios10

iOS 7
ios7

I've attempted to trace the leaks using the leak checker in Instruments, but I cannot seem to find anything substantial in the tests. All I can find using that tool is around 1KB of leaked objects per scan.

I guess my main question would be, is there a better way to trace the memory issue that appears to be tied to this scanner? Was there some kind of change in the way deallocation/ref counts are occurring in iOS 10 that would cause such a drastic change?

I've created a barebones cordova project with the above mentioned plugin, and implemented a basic scanner call into a button as shown in this code which should provide a reproducible test.

<!DOCTYPE html>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<html>
<head>
<!--
Customize this policy to fit your own app's needs. For more guidance, see:
https://github.com/apache/cordova-plugin-whitelist/blob/master/README.md#content-security-policy
Some notes:
* gap: is required only on iOS (when using UIWebView) and is needed for JS->native communication
* https://ssl.gstatic.com is required only on Android and is needed for TalkBack to function properly
* Disables use of inline scripts in order to mitigate risk of XSS vulnerabilities. To change this:
* Enable inline JS: add 'unsafe-inline' to default-src
-->

<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
<link rel="stylesheet" type="text/css" href="css/index.css">
<title>Hello World</title>
</head>
<body>
<div class="app">
<script type="text/javascript">
function scanTest(field) {
try {
debugger
var scanner = window.cordova.require("phonegap-plugin-barcodescanner.BarcodeScanner");
scanner.scan(
function (result) {
if (document.getElementById(field)) {
document.getElementById(field).innerHTML = result.text;
} else {
alert("ScannerCouldntIdentifyMessage");
}
});
} catch (e) {
alert("ErrorDescription: " + e);
}
}
</script>
<h1>Apache Cordova</h1>
<div id="deviceready" class="blink">
<p class="event listening">Connecting to Device</p>
<p class="event received">Device is Ready</p>
<p id="scan">Test scan</p>
<a href="#" onclick="scanTest('scan')" id="scantest">Scan</a>
</div>
</div>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/index.js"></script>
</body>
</html>


EDIT: After further investigation, I've noticed that there appears to be a rather large IOKit allocation in the VM that i'm not sure where it's coming from. You can see here from the VM Summary that with the iPad it is not allocating these 100 MiB chunks, but does so in iOS 10. I'm still not really that sure where this is coming from or what would force these extra calls.

iOS 10
ios10

iOS 7
ios7

Answer

After some discussion on github and further investigation, I found that the issue was caused by the plugin using the AVCaptureVideoDataOutputSampleBufferDelegate. It appeared that the video output settings were resulting in some leaks at the IOKit level.

The resolution to this was to update the plugin to use the AVCaptureMetadataOutputObjectsDelegate and update the output to use the AVCaptureMetadataOutput type. This required implementation of the method (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputMetadataObjects:(NSArray*)metadataObjects fromConnection:(AVCaptureConnection*)connection

This method was very similar to the previous one, but instead of using the zxing library to do the decoding, the decoding was done with a loop and AVMetadata classes:

  for (AVMetadataObject *metaData in metadataObjects) {
    AVMetadataMachineReadableCodeObject* code = (AVMetadataMachineReadableCodeObject*)
[self.previewLayer transformedMetadataObjectForMetadataObject:
(AVMetadataMachineReadableCodeObject*)metaData];

  if ([self checkResult:code.stringValue]) {
    [self barcodeScanSucceeded:code.stringValue format:[self formatStringFromMetadata:code]];
  }

These mentioned changes were released as part of v 6.0.3 of the plugin.