使用MonoTouch Core Graphics的应用程序神秘崩溃

时间:2010-05-24 04:47:29

标签: xamarin.ios cgbitmapcontext

我的应用程序使用视图控制器和包含按钮和子视图的简单视图启动。当用户触摸按钮时,子视图将填充滚动视图,该滚动视图显示电子表格的列标题,行标题和单元格。要绘制单元格,我使用CGBitmapContext绘制单元格,生成图像,然后将图像放入显示单元格的scrollview中包含的imageview。

当我在iPad上运行应用程序时,它会正常显示单元格,并且滚动视图允许用户在电子表格中滚动而没有任何问题。如果用户第二次触摸该按钮,则电子表格重绘并继续完美地工作。但是,如果用户第三次触摸该按钮,则应用程序崩溃。 “应用程序输出”窗口中没有显示异常信息。

我的第一个想法是连续按钮推送耗尽了所有可用内存,因此我在视图控制器中覆盖了DidReceiveMemoryWarning方法并使用断点来确认此方法未被调用。我的下一个想法是CGBitmapContext没有被释放,并寻找Monotouch相当于Objective C的CGContextRelease()函数。我能找到的最接近的是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 个答案:

答案 0 :(得分:1)

不确定这是否能解决你的问题(我比你更新),但似乎this answer MonoTouch更喜欢创建/发布图形上下文的不同范例, :

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

我不知道它是否正确地释放了所有东西,但是否则似乎有效。