Приложение с использованием MonoTouch Core Graphics таинственно вылетает - PullRequest
0 голосов
/ 24 мая 2010

Мое приложение запускается с контроллером представления и простым представлением, состоящим из кнопки и подпредставления. Когда пользователь касается кнопки, подпредставление заполняется представлениями прокрутки, которые отображают заголовки столбцов, заголовки строк и ячейки электронной таблицы. Чтобы нарисовать ячейки, я использую CGBitmapContext, чтобы нарисовать ячейки, сгенерировать изображение, а затем поместить изображение в представление изображения, содержащееся в просмотре прокрутки, который отображает ячейки.

Когда я запускаю приложение на iPad, оно прекрасно отображает ячейки, и просмотр прокрутки позволяет пользователю без проблем перемещаться по электронной таблице. Если пользователь нажимает кнопку второй раз, электронная таблица перерисовывается и продолжает отлично работать. Однако, если пользователь нажимает кнопку в третий раз, происходит сбой приложения. В окне «Вывод приложения» не отображается информация об исключениях.

Моей первой мыслью было, что последовательные нажатия кнопок занимают всю доступную память, поэтому я переопределил метод DidReceiveMemoryWarning в контроллере представления и использовал точку останова, чтобы подтвердить, что этот метод не вызывается. Моя следующая мысль состояла в том, что CGBitmapContext не был выпущен и искал Monotouch-эквивалент функции CGContextRelease () Objective C. Самым близким, что я смог найти, был метод экземпляра CGBitmapContext Dispose (), который я вызвал, не решая проблему.

Чтобы освободить как можно больше памяти (в случае, если у меня как-то не хватало памяти без предупреждения), я пытался принудительно собирать мусор каждый раз, когда заканчивал использовать CGBitmapContext. Это усугубило проблему. Теперь программа вылетает через мгновение после отображения таблицы в первый раз. Это заставило меня задуматься, собирал ли сборщик мусора что-то необходимое для продолжения отображения графики на экране.

Буду признателен за любые предложения относительно дальнейших путей расследования причин этих аварий. Я включил исходный код для класса SpreadsheetView. Соответствующим методом является DrawSpreadsheet (), который вызывается при касании кнопки.

Спасибо за вашу помощь в этом вопросе.

Стивен Эшли

  public class SpreadsheetView : UIView

 {
  public ISpreadsheetMessenger spreadsheetMessenger = null;
  public UIScrollView cellsScrollView = null;
  public UIImageView cellsImageView = null;

  public SpreadsheetView(RectangleF frame) : base()
  {
   Frame = frame;
   BackgroundColor = Constants.backgroundBlack;
   AutosizesSubviews = true;
  }

  public void DrawSpreadsheet()
  {
   UInt16 RowHeaderWidth = spreadsheetMessenger.RowHeaderWidth;
   UInt16 RowHeaderHeight = spreadsheetMessenger.RowHeaderHeight;
   UInt16 RowCount = spreadsheetMessenger.RowCount;
   UInt16 ColumnHeaderWidth = spreadsheetMessenger.ColumnHeaderWidth;
   UInt16 ColumnHeaderHeight = spreadsheetMessenger.ColumnHeaderHeight;
   UInt16 ColumnCount = spreadsheetMessenger.ColumnCount;

   // Add the corner
   UIImageView cornerView = new UIImageView(new RectangleF(0f, 0f,
      RowHeaderWidth, ColumnHeaderHeight));
   cornerView.BackgroundColor = Constants.headingColor;

   CGColorSpace cornerColorSpace = null;
   CGBitmapContext cornerContext = null;
   IntPtr buffer = Marshal.AllocHGlobal(RowHeaderWidth * ColumnHeaderHeight * 4);
   if (buffer == IntPtr.Zero)
    throw new OutOfMemoryException("Out of memory.");
   try
   {
    cornerColorSpace = CGColorSpace.CreateDeviceRGB();
    cornerContext = new CGBitmapContext
     (buffer, RowHeaderWidth, ColumnHeaderHeight, 8, 4 * RowHeaderWidth,
      cornerColorSpace, CGImageAlphaInfo.PremultipliedFirst);
    cornerContext.SetFillColorWithColor(Constants.headingColor.CGColor);
    cornerContext.FillRect(new RectangleF(0f, 0f, RowHeaderWidth, ColumnHeaderHeight));
    cornerView.Image = UIImage.FromImage(cornerContext.ToImage());
   }
   finally
   {
    Marshal.FreeHGlobal(buffer);
    if (cornerContext != null)
    {
     cornerContext.Dispose();
     cornerContext = null;
    }
    if (cornerColorSpace != null)
    {
     cornerColorSpace.Dispose();
     cornerColorSpace = null;
    }
   }
   cornerView.Image = DrawBottomRightCorner(cornerView.Image);
   AddSubview(cornerView);

   // Add the cellsScrollView
   cellsScrollView = new UIScrollView
    (new RectangleF(RowHeaderWidth, ColumnHeaderHeight,
     Frame.Width - RowHeaderWidth,
     Frame.Height - ColumnHeaderHeight));
   cellsScrollView.ContentSize = new SizeF
    (ColumnCount * ColumnHeaderWidth,
     RowCount * RowHeaderHeight);
   Size iContentSize = new Size((int)cellsScrollView.ContentSize.Width, 
                        (int)cellsScrollView.ContentSize.Height);
   cellsScrollView.BackgroundColor = UIColor.Black;
   AddSubview(cellsScrollView);

   CGColorSpace colorSpace = null;
   CGBitmapContext context = null;
   CGGradient gradient = null;
   UIImage image = null;
   int bytesPerRow = 4 * iContentSize.Width;
   int byteCount = bytesPerRow * iContentSize.Height;
   buffer = Marshal.AllocHGlobal(byteCount);
   if (buffer == IntPtr.Zero)
    throw new OutOfMemoryException("Out of memory.");

   try
   {
    colorSpace = CGColorSpace.CreateDeviceRGB();
    context = new CGBitmapContext
     (buffer, iContentSize.Width, 
      iContentSize.Height, 8, 4 * iContentSize.Width, 
      colorSpace, CGImageAlphaInfo.PremultipliedFirst);
    float[] components = new float[]
    {.75f, .75f, .75f, 1f,
     .25f, .25f, .25f, 1f};
    float[] locations = new float[]{0f, 1f};
    gradient = new CGGradient(colorSpace, components, locations);
    PointF startPoint = new PointF(0f, (float)iContentSize.Height);
    PointF endPoint = new PointF((float)iContentSize.Width, 0f);
    context.DrawLinearGradient(gradient, startPoint, endPoint, 0);
    context.SetLineWidth(Constants.lineWidth);
    context.BeginPath();
    for (UInt16 i = 1; i <= RowCount; i++)
    {
     context.MoveTo
      (0f, iContentSize.Height - i * RowHeaderHeight + (Constants.lineWidth/2));
     context.AddLineToPoint((float)iContentSize.Width, 
        iContentSize.Height - i * RowHeaderHeight + (Constants.lineWidth/2));
    }
    for (UInt16 j = 1; j <= ColumnCount; j++)
    {
     context.MoveTo((float)j * ColumnHeaderWidth - Constants.lineWidth/2, 
                    (float)iContentSize.Height);
     context.AddLineToPoint((float)j * ColumnHeaderWidth - Constants.lineWidth/2, 0f);
    }
    context.StrokePath();
    image = UIImage.FromImage(context.ToImage());
   }
   finally
   {
    Marshal.FreeHGlobal(buffer);
    if (gradient != null)
    {
     gradient.Dispose();
     gradient = null;
    }
    if (context != null)
    {
     context.Dispose();
     context = null;
    }
    if (colorSpace != null)
    {
     colorSpace.Dispose();
     colorSpace = null;
    }
    // GC.Collect();
    //GC.WaitForPendingFinalizers();
   }

   UIImage finalImage = ActivateCell(1, 1, image);
   finalImage = ActivateCell(0, 0, finalImage);
   cellsImageView = new UIImageView(finalImage);
   cellsImageView.Frame = new RectangleF(0f, 0f,
      iContentSize.Width, iContentSize.Height);
   cellsScrollView.AddSubview(cellsImageView);

  }

  private UIImage ActivateCell(UInt16 column, UInt16 row, UIImage backgroundImage)
  {
   UInt16 ColumnHeaderWidth = (UInt16)spreadsheetMessenger.ColumnHeaderWidth;
   UInt16 RowHeaderHeight = (UInt16)spreadsheetMessenger.RowHeaderHeight;

   CGColorSpace cellColorSpace = null;
   CGBitmapContext cellContext = null;
   UIImage cellImage = null;
   IntPtr buffer = Marshal.AllocHGlobal(4 * ColumnHeaderWidth * RowHeaderHeight);
   if (buffer == IntPtr.Zero)
    throw new OutOfMemoryException("Out of memory: ActivateCell()");
   try
   {
    cellColorSpace = CGColorSpace.CreateDeviceRGB();
    // Create a bitmap the size of a cell
    cellContext = new CGBitmapContext
     (buffer, ColumnHeaderWidth, RowHeaderHeight, 8,
      4 * ColumnHeaderWidth, cellColorSpace, CGImageAlphaInfo.PremultipliedFirst);
    // Paint it white
    cellContext.SetFillColorWithColor(UIColor.White.CGColor);
    cellContext.FillRect(new RectangleF(0f, 0f, ColumnHeaderWidth, RowHeaderHeight));
    // Convert it to an image
    cellImage = UIImage.FromImage(cellContext.ToImage());
   }
   finally
   {
    Marshal.FreeHGlobal(buffer);
    if (cellContext != null) 
    {
     cellContext.Dispose();
     cellContext = null;
    }
    if (cellColorSpace != null)
    {
     cellColorSpace.Dispose();
     cellColorSpace = null;
    }
    // GC.Collect();
    //GC.WaitForPendingFinalizers();
   }
   // Draw the border on the cell image
   cellImage = DrawBottomRightCorner(cellImage);

   CGColorSpace colorSpace = null;
   CGBitmapContext context = null;
   Size iContentSize = new Size((int)backgroundImage.Size.Width,
                                (int)backgroundImage.Size.Height);
   buffer = Marshal.AllocHGlobal(4 * iContentSize.Width * iContentSize.Height);
   if (buffer == IntPtr.Zero)
    throw new OutOfMemoryException("Out of memory: ActivateCell().");
   try
   {
    colorSpace = CGColorSpace.CreateDeviceRGB();
    // Set up a bitmap context the size of the whole grid
    context = new CGBitmapContext
     (buffer, iContentSize.Width, 
      iContentSize.Height, 8, 4 * iContentSize.Width, 
      colorSpace, CGImageAlphaInfo.PremultipliedFirst);
    // Draw the original grid into the bitmap
    context.DrawImage(new RectangleF(0f, 0f, iContentSize.Width, iContentSize.Height),
                      backgroundImage.CGImage);
    // Draw the cell image into the bitmap
    context.DrawImage(new RectangleF(column * ColumnHeaderWidth, 
                                     iContentSize.Height - (row + 1) * RowHeaderHeight,
                                     ColumnHeaderWidth, RowHeaderHeight),
                      cellImage.CGImage);
    // Convert the bitmap back to an image
    backgroundImage = UIImage.FromImage(context.ToImage());
   }
   finally
   {
    Marshal.FreeHGlobal(buffer);
    if (context != null)
    {
     context.Dispose();
     context = null;
    }
    if (colorSpace != null)
    {
     colorSpace.Dispose();
     colorSpace = null;
    }
    // GC.Collect();
    //GC.WaitForPendingFinalizers();
   }
   return backgroundImage;
  }

  private UIImage DrawBottomRightCorner(UIImage image)
  {
   int width = (int)image.Size.Width;
   int height = (int)image.Size.Height;
   float lineWidth = Constants.lineWidth;

   CGColorSpace colorSpace = null;
   CGBitmapContext context = null;
   UIImage returnImage = null;
   IntPtr buffer = Marshal.AllocHGlobal(4 * width * height);
   if (buffer == IntPtr.Zero)
    throw new OutOfMemoryException("Out of memory: DrawBottomRightCorner().");
   try
   {
    colorSpace = CGColorSpace.CreateDeviceRGB();
    context = new CGBitmapContext
     (buffer, width, height, 8, 4 * width, colorSpace, 
      CGImageAlphaInfo.PremultipliedFirst);
    context.DrawImage(new RectangleF(0f, 0f, width, height),
                      image.CGImage);
    context.BeginPath();
    context.MoveTo(0f, (int)(lineWidth/2f));

    context.AddLineToPoint(width - (int)(lineWidth/2f), (int)(lineWidth/2f));

    context.AddLineToPoint(width - (int)(lineWidth/2f), height);
    context.SetLineWidth(Constants.lineWidth);
    context.SetStrokeColorWithColor(UIColor.Black.CGColor);
    context.StrokePath();
    returnImage = UIImage.FromImage(context.ToImage());
   }
   finally
   {
    Marshal.FreeHGlobal(buffer);
    if (context != null){
     context.Dispose();
     context = null;}
    if (colorSpace != null){
     colorSpace.Dispose();
     colorSpace = null;}
    // GC.Collect();
    //GC.WaitForPendingFinalizers();
   }
   return returnImage;
  }
 }

1 Ответ

1 голос
/ 20 октября 2010

Не уверен, решит ли это вашу проблему (я даже новее в этом, чем вы), но из этого ответа кажется, что MonoTouch предпочитает другую парадигму для создания / выпуска графических контекстов, наряду строки:

UIGraphics.BeginImageContext(rect.Size)
var context = UIContext.GetCurrentContext();
// ... do stuff ...
UIImage image = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
// ... do something with image ...

Я не знаю, правильно ли оно все высвобождает, но в остальном это похоже на работу.

...