2010-05-24 7 views
0

Mon application se lance avec un contrôleur de vue et une vue simple composée d'un bouton et d'une sous-vue. Lorsque l'utilisateur touche le bouton, la sous-vue est remplie de scrollviews qui affichent les en-têtes de colonne, les en-têtes de ligne et les cellules d'une feuille de calcul. Pour dessiner les cellules, j'utilise CGBitmapContext pour dessiner les cellules, générer une image, puis mettre l'image dans l'imageview contenue dans le scrollview qui affiche les cellules. Lorsque j'exécute l'application sur l'iPad, les cellules s'affichent très bien et le scrollview permet à l'utilisateur de faire défiler la feuille de calcul sans problème. Si l'utilisateur appuie une deuxième fois sur le bouton, la feuille de calcul se redessine et continue de fonctionner parfaitement. Si, toutefois, l'utilisateur appuie une troisième fois sur le bouton, l'application se bloque. Il n'y a pas d'affichage des informations d'exception dans la fenêtre de sortie de l'application. Mon premier réflexe était que les poussées de boutons successives utilisaient toute la mémoire disponible, donc j'ai dépassé la méthode DidReceiveMemoryWarning dans le contrôleur de vue et utilisé un point d'arrêt pour confirmer que cette méthode n'était pas appelée. Ma pensée suivante était que le CGBitmapContext n'était pas libéré et cherchait un équivalent Monotouch de la fonction CGContextRelease() de l'Objective C. Le plus proche que j'ai pu trouver était la méthode d'instance CGBitmapContext Dispose(), que j'ai appelée, sans résoudre le problème. Afin de libérer autant de mémoire que possible (au cas où je manquerais de mémoire d'une façon ou d'une autre sans déclencher un avertissement), j'ai essayé de forcer la récupération de place chaque fois que j'ai fini d'utiliser un CGBitmapContext. Cela a aggravé le problème. Maintenant, le programme se bloque moment après l'affichage de la feuille de calcul la première fois. Cela m'a fait me demander si le Garbage Collector rassemblait quelque chose de nécessaire à l'affichage continu de graphiques sur l'écran.L'application utilisant MonoTouch Core Graphics se bloque mystérieusement

Je vous serais reconnaissant de toutes suggestions sur d'autres avenues pour enquêter sur la cause de ces accidents. J'ai inclus le code source de la classe SpreadsheetView. La méthode pertinente est DrawSpreadsheet(), qui est appelée lorsque le bouton est touché.

Nous vous remercions de votre aide à ce sujet.

Stephen Ashley

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

Vous devez reformater votre code pour qu'il soit lisible. Et c'est un très long bloc de code - pouvez-vous le raccourcir à la section pertinente? – Jason

+0

Encore trop de code. Essayez et affinez-le. –

Répondre

1

Je ne sais pas si cela va résoudre votre problème (je suis encore plus récente à ce que vous êtes), mais il semble de this answer que MonoTouch préfère un paradigme différent pour la création/graphiques libérant contextes, sur le modèle de:

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

Je ne sais pas si ça libère tout correctement, mais sinon cela semble fonctionner.