使用单触在循环中使用CGImage.ScreenImage时内存问题(Memory issue wh

2019-09-17 15:24发布

我试图创建一个应用程序中使用斑马线的MonoTouch的和C#端口读取QR代码,但我打的内存问题。 而应用程序处理捕获的画面帧的应用程序接收到存储器的警告,然后关闭。 我已删除调用斑马线追查其中的内存问题源于并能重现该问题只在一个循环中捕获屏幕图像。

下面是代码:

using System;
using System.Drawing;
using System.Collections.Generic;
using System.Threading;
using MonoTouch.UIKit;
using MonoTouch.Foundation;
using MonoTouch.CoreGraphics;
using com.google.zxing;
using com.google.zxing.common;
using System.Collections;
using MonoTouch.AudioToolbox;
using iOS_Client.Utilities;

namespace iOS_Client.Controllers
{

    public class CameraOverLayView : UIView
    {

        private Thread _thread;
        private CameraViewController _parentViewController; 
        private Hashtable hints;
        private static com.google.zxing.MultiFormatReader _multiFormatReader = null;
        private static RectangleF picFrame = new RectangleF(0, 146, 320, 157);
        private static UIImage _theScreenImage = null;


        public CameraOverLayView(CameraViewController parentController) : base()
        {
            Initialize();
            _parentViewController = parentController;
        }

        private void Initialize()
        {              

        }

        private bool Worker()
        {


            Result resultb = null;

            if(DeviceHardware.Version == DeviceHardware.HardwareVersion.iPhone4
               || DeviceHardware.Version == DeviceHardware.HardwareVersion.iPhone4S)
            {
                picFrame = new RectangleF(0, 146*2, 320*2, 157*2);

            }

                if(hints==null)
                {
                    var list = new ArrayList();

                    list.Add (com.google.zxing.BarcodeFormat.QR_CODE);

                    hints = new Hashtable();
                    hints.Add(com.google.zxing.DecodeHintType.POSSIBLE_FORMATS, list);
                    hints.Add (com.google.zxing.DecodeHintType.TRY_HARDER, true);
                }

                if(_multiFormatReader == null)
                {
                    _multiFormatReader = new com.google.zxing.MultiFormatReader();
                }

                using (var screenImage = CGImage.ScreenImage.WithImageInRect(picFrame))
                {
                    using (_theScreenImage = UIImage.FromImage(screenImage))
                    {
                        Bitmap srcbitmap = new System.Drawing.Bitmap(_theScreenImage);
                        LuminanceSource source = null;
                        BinaryBitmap bitmap = null;
                        try {
                            source = new RGBLuminanceSource(srcbitmap, screenImage.Width, screenImage.Height);
                            bitmap = new BinaryBitmap(new HybridBinarizer(source));

                            try {
                                    _multiFormatReader.Hints = hints;
                                    resultb = null;

                                    //_multiFormatReader.decodeWithState(bitmap);

                                    if(resultb != null && resultb.Text!=null)
                                    {

                                        InvokeOnMainThread( () => _parentViewController.BarCodeScanned(resultb));

                                    }
                                }
                            catch (ReaderException re)
                            {
                                //continue;
                            }

                        } catch (Exception ex) {
                            Console.WriteLine(ex.Message);
                        }

                        finally {
                            if(bitmap!=null)
                                bitmap = null;

                             if(source!=null)
                                source = null;

                            if(srcbitmap!=null)
                            {
                                srcbitmap.Dispose();
                                    srcbitmap = null;
                            }

                        }   

                    }
                }

            return resultb != null;
        }

        public void StartWorker()
        {
            if(_thread==null)
            {
                _thread = new Thread(()=> { 

                        bool result = false;
                        while (result == false)
                        {
                            result = Worker();
                            Thread.Sleep (67);
                        }               

                }); 

            }

            _thread.Start();            

        }

        public void StopWorker()
        {

            if(_thread!=null)
            {

                _thread.Abort();
                _thread = null;

            }

            //Just in case
            _multiFormatReader = null;
            hints = null;
        }

        protected override void Dispose(bool disposing)
        {
            StopWorker();   
            base.Dispose(disposing);
        }

    }
}

有趣的是我看了看http://blog.reinforce-lab.com/2010/02/monotouchvideocapturinghowto.html来试试,看看别人是怎么捕捉和处理视频和这个代码和我一样遭遇后,在约40戒烟内存警告秒。

希望的QR码将在40秒以内进行扫描,但我不知道,如果存储不断得到释放这么多的代码已被扫描后,问题可能会突然出现。 无论哪种方式,应该可以连续拍摄视频源无权内存问题?

Answer 1:

原来System.Drawing.Bitmap从zxing.MonoTouch因缺乏遭受Dispose这使得它永远不会释放它分配的非托管内存。

更近的一个(从你的链接)并释放非托管内存时Dispose (调用它的更好)。 但是它(用如创建一个位图上下文(在它的构造函数),并且不手动配置它using )。 因此,它依赖于垃圾收集器(GC)后做...

在许多情况下,这是不是一个大问题,因为GC会,最终释放此上下文实例,并且将回收相关的内存。 但是,如果你在一个循环中这样做有可能你会在GC踢之前耗尽(非托管)的内存,这将让你内存警告和iOS可以决定杀死你的应用程序(或它可以通过自身崩溃) 。

但我不知道,如果存储不断得到释放

是的,它应该是 - 但也许没有那么快,因为你需要记忆回来了。 实现(使用) IDisposable正确地将解决这个问题。

无论哪种方式,应该可以连续拍摄视频源无权内存问题?

是。 确保你有尽快,如释放你的内存using (var ...) { }并确保您使用不相同的第三方代码。



Answer 2:

这有点违反直觉的,但ScreenImage属性每次调用它会创建一个新的CGImage实例,所以你必须调用Dispose那个对象上,以及:

using (var img = CGImage.ScreenImage) {
    using (var screenImage = img.WithImageInRect(picFrame))
    {
    }
}


Answer 3:

我只想补充一点,为我工作相结合,从以前的答案信息的实际解决方案。 循环内的代码如下所示:

using (var pool = new NSAutoreleasePool ())
{
    using (var img = CGImage.ScreenImage)
    {       
        using (var screenImage = img.WithImageInRect(picFrame))
        {
            using (_theScreenImage = UIImage.FromImage(screenImage))
            {
            }
        }
    }
}

GC.Collect();


文章来源: Memory issue when using CGImage.ScreenImage in a loop using Mono Touch