Memory leaks 为什么即使使用非常简单的应用程序,MonoTouch也会导致大量内存泄漏(据仪器报告)?

Memory leaks 为什么即使使用非常简单的应用程序,MonoTouch也会导致大量内存泄漏(据仪器报告)?,memory-leaks,xamarin.ios,instruments,Memory Leaks,Xamarin.ios,Instruments,我正在用仪器运行一个非常基本的测试应用程序,它发现了很多内存泄漏!因为我知道苹果公司的人在向iTunes提交应用程序时会检查内存泄漏,所以我想调查一下这个问题 我的环境:在Mac OS X 10.6.6上使用MonoTouch 3.2.4开发MonoDevelop 2.4.2,目标是运行iOS 4.2.1的iPad 我的测试应用程序只是显示一个TableView,其中包含50个字符串的列表,并按它们的起始字母对它们进行分组 重现问题的步骤:使用MonoDevelop创建一个新的“基于iPad窗口

我正在用仪器运行一个非常基本的测试应用程序,它发现了很多内存泄漏!因为我知道苹果公司的人在向iTunes提交应用程序时会检查内存泄漏,所以我想调查一下这个问题

我的环境:在Mac OS X 10.6.6上使用MonoTouch 3.2.4开发MonoDevelop 2.4.2,目标是运行iOS 4.2.1的iPad

我的测试应用程序只是显示一个TableView,其中包含50个字符串的列表,并按它们的起始字母对它们进行分组

重现问题的步骤:使用MonoDevelop创建一个新的“基于iPad窗口的项目”,使用Interface Builder打开MainWindow.xib文件,在窗口上放置一个新的TableView,并为AppDelegate类创建其出口(名为“tview”)。 然后在Main.cs中输入以下代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using MonoTouch.Foundation;
    using MonoTouch.UIKit;

    namespace SimpleTable
    {
        public class Application
        {
            static void Main (string[] args)
            {
                UIApplication.Main (args);
            }
        }

        public partial class AppDelegate : UIApplicationDelegate
        {
            private List<string> _names;

            public override bool FinishedLaunching (UIApplication app, NSDictionary options)
            {
                _names = new List<string> { "Smith", "Jones", "Williams", "Brown", "Taylor",
                                            "Davies", "Wilson", "Evans", "Thomas", "Johnson",
                                            "Roberts", "Walker", "Wright", "Robinson", "Thompson",
                                            "White", "Hughes", "Edwards", "Green", "Hall",
                                            "Wood", "Harris", "Lewis", "Martin", "Jackson",
                                            "Clarke", "Clark", "Turner", "Hill", "Scott",
                                            "Cooper", "Morris", "Ward", "Moore", "King",
                                            "Watson", "Baker", "Harrison", "Morgan", "Patel",
                                            "Young", "Allen", "Mitchell", "James", "Anderson",
                                            "Phillips", "Lee", "Bell", "Parker", "Davis" };
                tview.Source = new MyTableViewSource(_names);

                window.MakeKeyAndVisible ();

                return true;
            }

            private class MyTableViewSource : UITableViewSource
            {
                private List<string> _sectionTitles;
                private SortedDictionary<int, List<string>> _sectionElements = new SortedDictionary<int, List<string>>();

                public MyTableViewSource(List<string> list)
                {
                    // Use LINQ to find the distinct set of alphabet characters required.
                    _sectionTitles = (from c in list select c.Substring(0, 1)).Distinct().ToList();

                    // Sort the list alphabetically.
                    _sectionTitles.Sort();

                    // Add each element to the List<string> according to the letter it starts with
                    // in the SortedDictionary<int, List<string>>.
                    foreach (string element in list)
                    {
                        int sectionNum = _sectionTitles.IndexOf(element.Substring(0, 1));
                        if (_sectionElements.ContainsKey(sectionNum)) 
                        {
                            // SortedDictionary already contains a List<string> for that letter.
                            _sectionElements[sectionNum].Add(element);
                        } 
                        else
                        {
                            // First time that letter has appeared, create new List<string> in the SortedDictionary.
                            _sectionElements.Add(sectionNum, new List<string> { element });
                        }
                    }
                }

                public override int NumberOfSections(UITableView tableView)
                {
                    return _sectionTitles.Count;
                }

                public override string TitleForHeader(UITableView tableView, int section)
                {
                    return _sectionTitles[section];
                }

                public override int RowsInSection(UITableView tableview, int section)
                {
                    return _sectionElements[section].Count;
                }

                public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
                {
                    string kCellIdentifier = "mycell";
                    UITableViewCell cell = tableView.DequeueReusableCell(kCellIdentifier);
                    if (cell == null)
                    {
                        // No re-usable cell found, create a new one.
                        cell = new UITableViewCell(UITableViewCellStyle.Default, kCellIdentifier);
                    }

                    string display = _sectionElements[indexPath.Section][indexPath.Row];
                    cell.TextLabel.Text = display;

                    return cell;
                }

                public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
                {
                    string display = _sectionElements[indexPath.Section][indexPath.Row];

                    showAlert("RowSelected", "You selected: \"" + display + "\"");

                    // Prevent the blue 'selection indicator' remaining.
                    tableView.DeselectRow(indexPath, true);
                }

                private void showAlert(string title, string message)
                {
                    using (var alert = new UIAlertView(title, message, null, "OK", null))
                    {
                        alert.Show();
                    }
                }
            }
        }
    }
程序,从仪器内部启动App:发现单一泄漏;似乎这个漏洞总是存在的,即使在测试一个空的应用程序时也是如此

  • 未注释

    public override string TitleForHeader(UITableView tableView, int section)
    
    程序,从仪器内部启动应用程序:发现大量泄漏,当上下滚动表格和/或选择任何一行时,泄漏数量增加

  • 替换

    return _sectionTitles[section];
    
    声明

    public override string TitleForHeader(UITableView tableView, int section)
    
    处理

    return "Test Header…";
    
    (因此使用常量字符串):与测试n.2中相同

  • 是MonoTouch被窃听了还是我忘记了一些重要的东西?如果即使是这样一个简单的应用程序在运行几分钟时也会产生数百次内存泄漏,那么一个真正的(更复杂的)应用程序会发生什么呢


    我在网上搜索了很多,但是我没有找到任何关于这个问题的重要帖子。。。任何贡献都将不胜感激。

    monotouch和mono vm分配和管理内存的方式尚未被仪器完全理解。苹果没有拒绝任何这方面的申请。如果您有一个您认为是泄漏的特定错误,请在monotouch bugtracker中提交一个问题,网址为


    话虽如此,我查看了您的屏幕截图,确实发现了可能发生的1个16字节泄漏,在您的第二个屏幕截图中发现了泄漏,并为即将发布的monotouch 4修复了泄漏。

    仅供参考;你的截图不符合你的答案,杰夫,但是。。。你说的“mono vm”到底是什么意思?据我所知,MonoTouch应用程序是提前编译的(苹果的SDK授权和iPhone/iPad开发不允许应用程序解释或动态编译代码)。我认为由MonoDevelop/MonoTouch编译的代码与由XCode/Objective-C编译的代码“无法区分”:这两种方法都应该生成ARM本机代码。。。(继续)我还认为C#代码(我正在简化)映射到了iOS本机API的调用,并且编译过程符合iOS本机内存管理模型(引用计数等)。从您的回复来看,似乎从C#source编译的二进制代码以不同于从Objective-C source编译的二进制代码的方式管理内存。这是真的吗?我很困惑。。。我在哪里可以找到关于这个问题的详尽解释?提前感谢…你有许多问题塞进了一个小小的评论区。Mono虚拟机仍然存在于每个应用程序中,但与AOT的汇编代码配合使用的只是C代码。托管对象有自己的对象堆,这是正确垃圾收集所必需的。这是一个高层次的解释,查看更多信息。正如我所说,你发现了2个有效的(尽管很小,16字节)泄漏,我已在即将发布的版本中修复。mono touch管理内存的方式没有人完全理解。任何一款应用程序,只要不在一段时间后显示“Hello world”应用程序,就会导致内存不足和崩溃。制作一个没有泄漏的应用程序所花费的时间比制作几个本机应用程序要多,再加上在本机中,您将以更小、更快和更少的电池消耗应用程序结束。
    return "Test Header…";