ios - Titanium MapView 不释放内存

标签 ios memory-management mobile titanium

我有一个应用程序是使用 Appcelerator Titanium(适用于 iOS,Ti 和 Apple 的最新 SDK)构建的,其中一部分严重依赖于 map 。我遇到的问题是,当我关闭包含 MapView 的窗口时,内存似乎没有释放。因此,在菜单屏幕和 map 之间来回切换会降低 iPhone 的速度,直到它最终完全停止响应(加载 3-5 个 map )。

我使用 Titanium 的 Ti.Platform.availableMemory 调用来查看进入带有 map 的窗口时以及 map 关闭后的内存。结果是每一次连续进入/退出都呈稳定下降趋势,沿着:

25(map.js 的初始加载)
20(注释后)
20(在 win.close() 之后)
19(第二次加载map.js)
18(注释)
19(离开)
18(输入)
16(注释)
15(离开)

在模拟器中,当窗口关闭时它可能会上升一点,但即使它显示出稳定的下降趋势。

这是 map 的代码,位于它自己的“map.js”文件中。我已将其精简为使用的功能代码(因此这里只有 button_index 的事件监听器)。

button_index.addEventListener('click', function()
{
    Ti.App.xhr.abort();
    if (mapview) {
        mapview.removeAllAnnotations();
        mapview = null;
    }
    if(policeJson){
        policeJson = null;
        fireJson = null;
    }
    Ti.App.police = false;
    Ti.App.types = null;
    win.close(); //This should clean up everything, according to the docs
    Ti.API.info('Memory: ' + Ti.Platform.availableMemory);
});

var mapview;// = Ti.App.mapview;

Titanium.Geolocation.purpose = "Recieve User Location";
Titanium.Geolocation.accuracy = Titanium.Geolocation.ACCURACY_BEST;
Ti.API.info('Memory: ' + Ti.Platform.availableMemory);

function getMarkers(e){
    var miles = Ti.App.miles;
    Ti.API.info("Getting markers");
    //Google's API radius is in meters, so we need to convert
    var radius = miles * 1610; // 1mi = 1609.344 meters, so we just round up to 1610.
    // http connection setup
    Ti.App.xhr.setTimeout(10000);

    googleLatLng = e.coords.latitude + "," + e.coords.longitude;

    Ti.App.xhr.onload = function()
    {
        var data = Ti.XML.parseString(this.responseText);

        var ref = data.documentElement.getElementsByTagName("reference");
        if(ref != null && Ti.App.xhr.readyState == 4){
            for(var i =0; i < ref.length; i++){
                var marker = new Object();
                marker.lat = data.documentElement.getElementsByTagName("lat").item(i).text;
                marker.lng = data.documentElement.getElementsByTagName("lng").item(i).text;
                marker.name = data.documentElement.getElementsByTagName("name").item(i).text;
                marker.ref = ref.item(i).text;
                addMarker(marker);
                marker = null;
            }
        }
    };
    Ti.App.xhr.open("GET","https://maps.googleapis.com/maps/api/place/search/xml?location=" + googleLatLng + "&radius=" + radius + "&types=" + Ti.App.types + "&sensor=true&key=" + Ti.App.apiKey,false);

    Ti.App.xhr.send();
    Ti.API.info('Memory: ' + Ti.Platform.availableMemory);
}

// find the user's location and mark it on the map
function waitForLocation(e)
{
    var region = null;
    if ( e.error ) {
        region = regionDefault; // User didn't let us get their location
        var alertDialog = Titanium.UI.createAlertDialog({
            title: 'Geolocation',
            message: 'We were unable to center the map over your location.',
            buttonNames: ['OK']
        });
        alertDialog.show();
    } else {
        region = {
            latitude: e.coords.latitude,
            longitude: e.coords.longitude,
            animate:true,
            latitudeDelta:0.05,
            longitudeDelta:0.05
        };
    }
    Ti.App.lat = region.latitude;
    Ti.App.lng = region.longitude;
    mapview.setLocation(region);

    mapview.removeAllAnnotations();
    currentLoc = Titanium.Map.createAnnotation({
        latitude: region.latitude,
        longitude: region.longitude,
        title: e.error ? "Columbus" : "You are here!",
        pincolor: Titanium.Map.ANNOTATION_RED,
        animate:true
    });
    mapview.addAnnotation(currentLoc);
    mapview.selectAnnotation(currentLoc);
    mapview.addEventListener('click', function(e){
        if (e.clicksource == 'rightButton') {
            if (e.annotation.spotUrl != '') {
                alert('Website!');
            }
            else {
                alert('No website available');
            }
        }
    });

    if(Ti.App.police == true) {
        var fire_img = "../../images/iNeighborhood/fire.png";
        var police_img = "../../images/iNeighborhood/police.png";
        serviceMarkers(fire_addr, fire_title, fire_lat_1, fire_long_1,fire_img);
        serviceMarkers(police_addr, police_title, police_lat_1, police_long_1,police_img);
    }

    getMarkers(e);
}


function addMarker(marker){
    var ann = Titanium.Map.createAnnotation({
        animate:true,
        latitude:marker.lat,
        longitude:marker.lng,
        title:marker.name,
        pincolor: Titanium.Map.ANNOTATION_GREEN
    });

    mapview.addAnnotation(ann);
}

// Automatically refresh current location.
/*
 * IN PROGRESS
 */
function getLocation(){
    // create the mapView and center it on Columbus
    if (!mapview) {
        mapview = Titanium.Map.createView({
            mapType: Titanium.Map.STANDARD_TYPE,
            animate: true,
            region: {
                latitude: 39.961176,
                longitude: -82.998794,
                latitudeDelta: 0.1,
                longitudeDelta: 0.1
            },
            regionFit: true,
            userLocation: true,
            visible: true,
            top:29
        });

        //Ti.App.mapview = mapview;

        win.add(mapview);
    }
    refresh();

    //Get the current position and set it to the mapview
    Titanium.Geolocation.getCurrentPosition(waitForLocation);
}
getLocation();

// pretty self explanatory...
function cleanMap(){
    if (mapview) {
        mapview.removeAllAnnotations();
    }
    if(xhr){
        xhr.abort();
    }
}

Ti.App.addEventListener('map:mapIt',function(){
    cleanMap();
    getLocation();
});

这是加载 map 的索引页面中的一些代码:

var winMap = Titanium.UI.createWindow({
    url:'map.js',
    tabBarHidden:false
});

btnEducation.addEventListener('click',function(){
    Ti.App.types = Ti.App.schools;
    Ti.UI.currentTab.open(winMap);
    Ti.App.police = false;
});

我制作了一个全局 HTTPClient 并重用它,就像其他一些问答答案(在 SO 和 Appcelerator 的网站上)所建议的那样,这似乎有所帮助(每次 map 加载不会耗尽太多内存),我还尝试手动将变量(尤其是较大的变量)设置为 null(这可能有效也可能无效),但仍然存在一些问题。我还尝试在事件监听器中为打开窗口的按钮创建 map 窗口,但这似乎根本没有任何效果。

我还运行了 Instruments 看看它能找到什么,但没有发现任何值得注意的东西(我什至把它展示给我的同事,他全职做移动开发,他说他没有发现任何异常可以看到)。

我已经查看这段代码几个小时了,这不是我的全部代码,所以我完全有可能遗漏了一些明显的东西,但是我的代码中是否有原因导致内存不可用应该释放吗?还有什么我可以做的来释放更多的内存吗?我现在只针对 iOS 进行开发,因此可以接受特定于 iOS 的解决方案。

编辑 - 我现在还尝试将 map 部分包含到调用它的文件中(使用 Ti.include('map.js'))。我做了一个快速而肮脏的设置,看看它是否有效:

Ti.include('map.js');
var button_index = Ti.UI.createButton({
   text:'Back',
   height:20,
   width:50,
   top:0,
   left:0,
   color:'#000'
});
button_index.addEventListener('click', function()
{
    Ti.App.xhr.abort();
    if (mapview) {
        mapview.removeAllAnnotations();
//        mapview = null;
    }
    if(policeJson){
        policeJson = null;
        fireJson = null;
    }
    Ti.App.police = false;
    Ti.App.types = null;
    Ti.App.title = null;
    mapview.hide();
    Ti.API.info('Memory: ' + Ti.Platform.availableMemory);
});
mapview.add(button_index);
mapview.hide();

btnArts.addEventListener('click',function(){
    Ti.App.types = Ti.App.arts;
//    Ti.UI.currentTab.open(winMap);
mapview.show();
Ti.App.fireEvent('map:mapIt'); //Triggers the chain of events to clear the map and add the necessary annotations to it
    Ti.App.police = false;
    Ti.App.title = 'arts';
});

它似乎工作得更好,但是当我进出 mapview 时,可用内存量仍然在稳步减少,并且初始内存负载使得它在设备上与其他方法一样无法使用(丢弃内存减少到大约 3MB)。

最佳答案

来自关于选项卡/选项卡组的文档...“一个 TabGroup Tab 实例。每个 Tab 实例维护一堆选项卡窗口。一次只能看到选项卡中的一个窗口。当窗口关闭时,由用户或代码将窗口从堆栈中移除,使前一个窗口可见。”

一种猜测是 close() 在应用于选项卡时可能不会按照您假设的方式运行,因为它似乎在您在选项卡之间循环时保持选项卡之间的状态。另外,也许上面的代码示例中缺少一些东西,但我实际上并没有看到在哪里 “win”被定义为一个变量(我假设你在某处有 var win = Ti.UI.currentWindow(); 但你可能想仔细检查它是否真的在该函数时关闭被称为。

您还可以考虑为您的应用程序创建单个对象,并将函数链接到该对象,以免污染全局范围。请参阅:http://wiki.appcelerator.org/display/guides/JavaScript+Best+Practices

关于ios - Titanium MapView 不释放内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6538555/

相关文章:

iPhone 电池耗尽问题

ios - 无法让委托(delegate)在我的观点之间工作

ios - ASCellNode 内部水平布局显示错误

c - 避免在函数调用之间传递大数据

java - 局部变量、对象引用及其内存分配

java - 更改 Java Android 中的 UI

ios - 2 个或更多具有相同 swift 类崩溃的 View Controller

c++ - 使用 new 为 unsigned char 数组分配内存失败

android - 使用 Android 从 SIM 卡获取 MSISDN

javascript - 关于输入类型="number"在手机中打开数字键盘但如何打开输入类型="password"的小键盘