2

Monotouch と Zxing の C# ポートを使用して QR コードを読み取るアプリを作成しようとしていますが、メモリの問題が発生しています。アプリがキャプチャされた画面フレームを処理している間、アプリはメモリ警告を受け取り、シャットダウンされます。Zxing への呼び出しを削除して、メモリの問題の原因を突き止め、画面イメージをループでキャプチャするだけで問題を再現できるようにしました。

コードは次のとおりです。

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秒以内にスキャンされることを願っていますが、メモリが解放されるかどうかはわかりませんので、多くのコードがスキャンされた後に問題が発生する可能性があります. いずれにせよ、メモリの問題なしに連続してビデオ フィードをキャプチャできるはずですよね?

4

3 に答える 3

3

これはやや直感に反しますが、ScreenImage プロパティは呼び出すたびに新しい CGImage インスタンスを作成するため、そのオブジェクトでも Dispose を呼び出す必要があります。

using (var img = CGImage.ScreenImage) {
    using (var screenImage = img.WithImageInRect(picFrame))
    {
    }
}
于 2012-05-30T21:39:50.797 に答える
2

以前の回答からの情報を組み合わせた、私にとってうまくいった実際の解決策を追加します。ループ内のコードは次のようになります。

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

GC.Collect();
于 2012-05-31T09:27:38.257 に答える
1

System.Drawing.Bitmapzxing.MonoTouchのオリジナルDisposeには、割り当てられたアンマネージ メモリを決して解放しないという問題がありました。

最近のもの(リンクから)は、Dispose呼び出されたときに管理されていないメモリを解放します(より良いです)。ただし、ビットマップコンテキストを(コンストラクターで)作成し、手動で(たとえば、を使用してusing)破棄しません。したがって、後で行うにはガベージコレクター(GC)に依存しています...

多くの場合、GC は最終的にこのコンテキスト インスタンスを解放し、関連するメモリを再利用するため、これは大きな問題ではありません。ただし、これをループで実行している場合、GC が開始される前に (管理されていない) メモリが不足する可能性があります。これにより、メモリの警告が表示され、iOS はアプリケーションを強制終了することを決定できます (または、アプリケーション自体がクラッシュする可能性があります) 。 .

しかし、メモリが解放されるかどうかはわかりません

はい、そうなるはずですが、メモリを取り戻す必要があるほど速くはないかもしれません。IDisposable正しく実装 (および使用)すると、これが解決されます。

いずれにせよ、メモリの問題なしに連続してビデオ フィードをキャプチャできるはずですよね?

はい。などを使用して、できるだけ早くメモリを解放していることを確認しusing (var ...) { }、使用するサードパーティのコードが同じことを行うようにしてください。

于 2012-05-30T15:05:51.320 に答える