1

下面的代码变体曾经可以工作,但现在我得到了内存泄漏,如下面的屏幕截图所示。我将代码从不同的线程移动到 TimerElapsed 事件(参见屏幕截图中的堆栈跟踪),对 MD 3.0.3.4 和 MT 5.3.4 进行了各种代码更新和升级。不幸的是,我似乎无法弄清楚为什么它不再起作用了。恢复到从常规线程调用的以前版本的代码似乎也不起作用。这是当前版本的 MD 或 MT 中的错误吗?

如您所见,我正在使用 NSAutoReleasePool 并在最后强制进行垃圾收集,但它仍然不起作用!

编辑:我在下面添加了代码,通过 DrawCustomImage 从其声明中跟踪 unpackedImage,然后作为“imageToSet”参数进入 SetImages(),然后作为“overlay”参数进入 overlayImage()。SetImage 在主线程上调用其中的代码,因为当它最终调用 UpdateLiveScreen(带有生成的 overlayedImage)时,它实际上是在屏幕上绘制的。

    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;
    }

在此处输入图像描述

4

1 回答 1

0

Thanks for filling a bug report.

The native call to CGBitmapContextCreateImage (from the Instruments' stack trace) is being done when your call ToImage (see source). That code has not changed between 5.2.12 and 5.3 so it's likely not the culprit.

The managed GCImage does not take an extra reference to the handle (true is used in the .ctor) (see source, no recent change either).

So this takes us to the unpackedImage UIImage that you return from your method. There's a change (in 5.3.x) where an automatic NSAutoreleasePool is used around this call.

Otherwise it ends up in where/how you deal with the returned unpackedImage (and on which thread, e.g. CoreGraphics is thread-safe while UIKit mostly is not).

Could you add the code (here or privately on the bug report) showing this ? I don't believe the issue is with your code (since it works on 5.2.x) but it would make tracking this a lot easier.

Note that calling GC.Collect is not helpful (and it's rarely is) since the instances you're using all implement IDisposable (which generally immediately dispose of the resources and remove the finalization step) and you dispose them manually or with using.

于 2012-07-22T15:18:09.967 回答