2010-05-24 74 views
0

我的應用程序將啓動一個視圖控制器和一個由按鈕和子視圖組成的簡單視圖。當用戶觸摸該按鈕時,子視圖中將顯示滾動視圖,該滾動視圖顯示電子表格的列標題,行標題和單元格。爲了繪製單元格,我使用CGBitmapContext繪製單元格,生成圖像,然後將圖像放入顯示單元格的scrollview中包含的圖像視圖中。使用MonoTouch Core Graphics的應用程序神祕崩潰

當我在iPad上運行應用程序時,它顯示單元格很好,滾動視圖讓用戶在電子表格中滾動而不出現任何問題。如果用戶第二次觸摸該按鈕,電子表格將重新繪製並繼續完美工作,但如果用戶第三次觸摸該按鈕,則應用程序會崩潰。 Application Output窗口中沒有異常信息顯示。

我的第一個想法是,連續的按鈕按下了所有可用的內存,所以我忽略了視圖控制器中的DidReceiveMemoryWarning方法,並使用了一個斷點來確認這個方法沒有被調用。我的下一個想法是,CGBitmapContext沒有被髮布,並且尋找與Objective C的CGContextRelease()函數相同的Monotouch。我能找到的最接近的是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; 
    } 
} 
+0

您需要重新格式化您的代碼以使其可讀。這是一段很長的代碼 - 你能縮短到相關部分嗎? – Jason 2010-05-24 17:16:27

+0

代碼太多了。嘗試並縮小範圍。 – 2010-09-14 21:31:18

回答

1

不知道這是否會解決您的問題(我比你甚至更新到這一點),但它從this answer是MonoTouch的喜歡創建/釋放顯卡不同的範式似乎背景,沿着線:

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

我不知道,如果它的正確釋放的一切,但除此之外,它似乎工作。