Почему MonoTouch вызывает много утечек памяти (как сообщает Instruments) даже с очень простыми приложениями? - PullRequest
3 голосов
/ 15 февраля 2011

Я запускаю очень простое тестовое приложение с инструментами и обнаруживает много утечек памяти!Поскольку я знаю, что ребята из Apple выполняют проверки на утечку памяти при отправке приложения в iTunes, я хотел бы исследовать эту проблему.

Моя среда: MonoDevelop 2.4.2 с MonoTouch 3.2.4 на Mac OS X 10.6.6 для iPad под управлением iOS 4.2.1.

Мое тестовое приложение просто показывает TableView, заполненный списком из 50 строк, сгруппировав их по начальной букве.

Шаги для воспроизведенияпроблема: создайте новый «iPad Window-based Project» с MonoDevelop, откройте файл MainWindow.xib с Interface Builder, поместите новый TableView в окно и создайте его выход (с именем «tview») для класса AppDelegate.Затем введите следующий код в 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();
                    }
                }
            }
        }
    }

Я выполнил следующие тесты на устройстве:

  1. закомментировано

    public override string TitleForHeader(UITableView tableView, int section)
    

    процедура, запущенная App изнутри Instruments: обнаружена единственная утечка;Кажется, что эта утечка ВСЕГДА присутствует, даже при тестировании пустого приложения!

    Скриншот Test 1 Instruments

  2. без комментариев

    public override string TitleForHeader(UITableView tableView, int section)
    

    процедура, запущенная изнутри приложения App: обнаружено много утечек, и их количество увеличивается при прокрутке таблицы вверх и вниз и / или выборе любой строки.

    Test 2 Instrumentsснимок экрана

  3. заменил оператор

    return _sectionTitles[section];
    

    в процедуре

    public override string TitleForHeader(UITableView tableView, int section)
    

    на

    return "Test Header…";
    

    (таким образом используя постоянную строку): так же, как в тесте №2!

Ошибка MonoTouch или я забыл что-то важное?Если даже такое простое приложение генерирует сотни утечек памяти при работе в течение нескольких минут, что может случиться с реальным (и более сложным) приложением?

Я много искал в Интернете, но не нашел ни одноговажный пост об этой проблеме ... любой вклад будет принята с благодарностью.

1 Ответ

3 голосов
/ 15 февраля 2011

Способ, которым monotouch и mono vm распределяют память и управляет ею, не до конца понятен инструментам.Apple не отклонила ни одного приложения для этого.Если у вас есть конкретная ошибка, которая, по вашему мнению, является утечкой, сообщите об этом в багтрекер monotouch по адресу http://monotouch.net/Support

При этом я посмотрел на ваши скриншоты и обнаружил, что может произойти утечка в 16 байти утечка на вашем втором скриншоте, и исправили это для грядущего монотонья 4.

...