2012-07-22 5 views
1

Une variante du code ci-dessous utilisé pour fonctionner, mais maintenant je reçois une fuite de mémoire comme vous pouvez le voir dans la capture d'écran ci-dessous. J'ai déplacé le code d'un thread différent à un événement TimerElapsed (voir trace de la pile dans la capture d'écran), fait diverses mises à jour de code et mises à niveau vers MD 3.0.3.4 et MT 5.3.4. Malheureusement, je n'arrive pas à comprendre pourquoi ça ne marche plus. Revenir à une version précédente du code où il est appelé à partir d'un fil régulier ne semble pas fonctionner non plus. Est-ce un bug dans la version actuelle de MD ou MT? Comme vous pouvez le voir, j'utilise NSAutoReleasePool en plus de forcer un garbage collect à la fin et ça ne marche toujours pas!fuite de mémoire Monotouch

EDIT: Je ai ajouté le code ci-dessous qui retrace l'unpackedImage de sa déclaration par DrawCustomImage, puis dans SetImages() comme paramètre "imageToSet", puis dans overlayImage() comme paramètre "overlay". SetImage appelle le code à l'intérieur sur le thread principal car lorsqu'il appelle finalement UpdateLiveScreen (avec l'overlayedImage qui en résulte), il dessine réellement à l'écran.

static UIImage unpackedImage = new UIImage(); 

    public static void DrawCustomImage(IntPtr buffer, int width, int height, int bytesPerRow, CGColorSpace colSpace, byte[] rawPixels, ref UIImage unpackedImage) 
    { 
     using (var pool = new NSAutoreleasePool()) 
     { 
      GCHandle pinnedArray = GCHandle.Alloc(rawPixels, GCHandleType.Pinned); 
      IntPtr pointer = pinnedArray.AddrOfPinnedObject(); 

      // Set a grayscale drawing context using the image buffer 
      CGBitmapContext context = new CGBitmapContext(pointer, width, height, 8, bytesPerRow, colSpace, CGImageAlphaInfo.None); 

      try 
      { 
       // Convert the drawing context to an image and set it as the unpacked image 
       //using (var pool = new NSAutoreleasePool()) 
       { 
        using (var img = context.ToImage()) 
        { 
         unpackedImage = UIImage.FromImage(img); 
        } 
       } 
      } finally 
      { 
       pinnedArray.Free(); 
       if (context != null) 
        context.Dispose(); 
      } 
     } 
     GC.Collect(); 
    } 

    SetImages(labelText, symbolArray[0], unpackedImage, points); 

    public static void SetImages(String labelText, UIImage symbol, UIImage imageToSet, PointF[] points) 
    { 
     appReference.InvokeOnMainThread(delegate 
     { 
      int imageWidth = 716; 
      int imageHeight = (int)imageToSet.Size.Height; 
          int nextFreeMainImageColumn = 5; // This gets set dynamically, but is simplified here for readability 

      lock (displayLocker) 
      { 
       // Get the current doppler image 
       UIImage mainImage = GetMainImage(); 

       // Add the new imageToSet to the current image by overlaying it adjacent to the current image 
       UIImage overlayedImage = overlayImage(mainImage, imageToSet, 
            new RectangleF(0, 0, imageWidth, imageHeight), 
            new RectangleF(nextFreeMainImageColumn, 0, imageToSet.Size.Width, imageHeight)); 

       // Update the live screen with the updated image and frame number 
       LiveCont.UpdateLiveScreen(labelText, symbol, overlayedImage, points); 
      } 
     }); 
    } 

    public static UIImage overlayImage(UIImage image, UIImage overlay, RectangleF imageBoundingBox, RectangleF overlayBoundingBox) 
    { 
     int numBytes = 4; // Four bytes per pixel for a color image (Alpha, Red, Green, Blue) 
     int bytesPerRow = (int)imageBoundingBox.Width * numBytes; 

     // Set a color drawing context 
     CGBitmapContext context = new CGBitmapContext(
      IntPtr.Zero, 
      (int)imageBoundingBox.Width, 
      (int)imageBoundingBox.Height, 
      8, 
      bytesPerRow, 
      CGColorSpace.CreateDeviceRGB(), 
      CGImageAlphaInfo.NoneSkipFirst 
     ); 

     UIImage overlayedImage = null; 
     try 
     { 
      context.DrawImage(imageBoundingBox, image.CGImage);    // Draw the main image 
      context.DrawImage(overlayBoundingBox, overlay.CGImage);   // Draw the overlay 

      using (var img = context.ToImage()) 
      { 
       overlayedImage = UIImage.FromImage(img);     // Convert the context back to an image 
      } 
     } 
     finally 
     { 
      if (context != null) 
       context.Dispose(); 
      image.Dispose(); 
     } 

     return overlayedImage; 
    } 

enter image description here

+0

Je suis revenu à MT 5.2.12 et le problème a disparu, donc il doit y avoir un bug dans le code MT entre MT 5.2.12 et 5.3.4. – nbonwit

Répondre

0

Merci de remplir un bug report.

L'appel natif à CGBitmapContextCreateImage (à partir de la trace de la pile d'Instruments) est en cours lorsque vous appelez ToImage (voir source). Ce code n'a pas changé entre 5.2.12 et 5.3, donc ce n'est probablement pas le coupable.

Le géréGCImage ne prend pas une référence supplémentaire à la poignée (true est utilisé dans le .ctor) (voir source, aucun changement récent non plus).

Cela nous amène au unpackedImageUIImage que vous revenez de votre méthode. Il y a un changement (en 5.3.x) où un automatiqueNSAutoreleasePool est utilisé autour de cet appel.

Sinon, il se retrouve dans où/comment vous traitez avec le retour unpackedImage (et sur lequel fil, par exemple CoreGraphics est thread-safe tandis que la plupart du temps UIKit est pas).

Pourriez-vous ajouter le code (ici ou en privé sur le rapport de bogue) le montrant? Je ne crois pas que le problème soit lié à votre code (puisque cela fonctionne sur 5.2.x), mais cela rendrait le suivi beaucoup plus facile.

Notez que l'appel GC.Collect n'est pas utile (et il est rarement) puisque les instances que vous utilisez tous les mettre en œuvre IDisposable (qui généralement disposer immédiatement des ressources et supprimer l'étape de finalisation) et vous les disposer manuellement ou avec .