1

元の画像を取得して、3つの異なるズームスケール-> 16倍、10倍、4倍にサイズ変更する関数を作成しました。理解を深めるために、この段落を読み続けてください。元の画像が1000x1000だとしましょう。1倍ズームでの寸法は50x50になることを宣言します。つまり、4倍ズームは200x200、10倍ズームは500x500、16倍ズームは800x800になります。したがって、私の関数は、元の1000x1000を800x800に、次に500x500に、次に200x200にサイズ変更する必要があります。私はこれを正常に実行し、私の質問はメモリ使用量に関するものであることに注意してください。

以下に2つの方法があります。どちらの方法も機能しますが、一方が他方よりも約3x / 4倍多くのメモリを使用して大量のメモリ使用量を膨らませます...3つの画像のそれぞれのサイズを変更しないため、最初の方法よりも大幅に高速に読み込まれるため、2番目の方法の方が好きです。元の画像からではなく、以前にサイズ変更された画像からサイズを変更します。

注:メモリ使用量を測定するためにXcodeInstrumentsを使用しています。ImageResizerクラスには、画像のサイズを変更する「サイズ変更」と呼ばれる関数が含まれています。

方法1.)

    public List<UIImage> InitImageList_BFObjects ( UIImage image, SizeF frameSize )
    {
        List<UIImage> listOfImages = new List<UIImage>();

        for ( int i = 0; i < 3; i++ )
        {
            if ( i == 0 )
                zoomScale = 16f;
            else if ( i == 1 )
                zoomScale = 10f;
            else// if ( i == 2 )
                zoomScale = 4f;

            Resizer = new ImageResizer(image);
            Resizer.Resize(frameSize.Width * zoomScale, frameSize.Height * zoomScale);
            UIImage resizedImage = Resizer.ModifiedImage;
            listOfImages.Insert(0, resizedImage);

        }

        return listOfImages;
    }

方法1は機能し、メモリ使用量はごくわずかです。私はこれを約20枚の画像のグループで実行しました。これがロードされた後、私のアプリには約14MBのメモリ使用量がありました(Xcodes Instrumentsを使用してメモリ使用量を調べます)

方法2.)

    public List<UIImage> InitImageList_BFObjects ( UIImage image, SizeF frameSize )
    {
        List<UIImage> listOfImages = new List<UIImage>();

        for ( int i = 0; i < 3; i++ )
        {
            if ( i == 0 )
                zoomScale = 16f;
            else if ( i == 1 )
                zoomScale = 10f;
            else// if ( i == 2 )
                zoomScale = 4f;


            if ( listOfImages.Count == 0 )
            {
                Resizer = new ImageResizer(image);
                Resizer.Resize(frameSize.Width * zoomScale, frameSize.Height * zoomScale);
                UIImage resizedImage = Resizer.ModifiedImage;
                listOfImages.Insert(0, resizedImage);
            }
            else
            {
                // THIS LINE CONTAINS THE MAIN DIFFERENCE BETWEEN METHOD 1 AND METHOD 2
                // Notice how it resizes from the most recent image from listOfImages rather than the original image
                Resizer = new ImageResizer(listOfImages[0]);
                Resizer.Resize(frameSize.Width * zoomScale, frameSize.Height * zoomScale);
                UIImage resizedImage = Resizer.ModifiedImage;
                listOfImages.Insert(0, resizedImage);
            }
        }

        return listOfImages;
    }

方法2は機能しますが、メモリ使用量は急上昇します。私はこれを約20枚の画像の同じグループで実行しました。これがロードされた後、私のアプリのメモリ使用量は60MBを超えました(Xcodes Instrumentsを使用してメモリ使用量を調べます)なぜメモリ使用量が非常に高いのですか?記憶がロケットに飛ぶ原因となる方法2についてはどうですか?変数が適切にクリーンアップされていないようです

*追加情報、ImageResizerクラス* *

ImageResizerクラスから不要な関数を切り取り、名前を「ImageResizer_Abridged」に変更しました。誤って必要なものを切り取らないようにするために、このクラスの使用に切り替えました。

public class ImageResizer_Abridged
{
    UIImage originalImage  = null;
    UIImage modifiedImage = null;

    public ImageResizer_Abridged ( UIImage image )
    {
        this.originalImage = image;
        this.modifiedImage = image;
    }   

    /// <summary>
    /// strech resize
    /// </summary>
    public void Resize( float width, float height )
    {
        UIGraphics.BeginImageContext( new SizeF( width, height ) );
        //
        modifiedImage.Draw( new RectangleF( 0,0, width, height ) );
        modifiedImage = UIGraphics.GetImageFromCurrentImageContext();
        //
        UIGraphics.EndImageContext();
    }

    public UIImage OriginalImage 
    {
        get 
        {
            return this.originalImage;
        }
    }

    public UIImage ModifiedImage 
    {
        get 
        {
            return this.modifiedImage;
        }
    }
}

この問題を示す簡略化されたテストプロジェクトを作成しました*

プロジェクトへのドロップボックスリンクは次のとおりです: https ://www.dropbox.com/s/4w7d87nn0aafph9/TestMemory.zip

これが証拠としてのメソッド1のXcodeInstrumentsスクリーンショットです(9 mbのメモリ使用量):http: //i88.photobucket.com/albums/k194/lampshade9909/AllImagesResizedFromOriginalImage_zps585228c6.jpg

証拠としてホットなメソッド2のXcodeInstruments画面は次のとおりです(55 mbのメモリ使用量):http: //i88.photobucket.com/albums/k194/lampshade9909/SignificantIncreaseInMemoryUsage_zps19034bad.jpg

以下は、テストプロジェクトを実行するために必要なコードブロックです

        // Initialize My List of Images
        ListOfImages = new List<UIImage>();

        for ( int i = 0; i < 30; i++ )
        {
            // Create a UIImage Containing my original Image
            UIImage originalImage = UIImage.FromFile ("b2Bomber.png");
            float newWidth = 100f;
            float newHeight = 40f;
            float zoomScale;
            float resizedWidth, resizedHeight;

            UIImage resizedImage1;
            UIImage resizedImage2;

            // Basically, I want to take the originalImage Image and resize it twice.  
            // Method 1.) Resize the originalImage and save it as ResizedImage1.  Resize the originalImage and save it as ResizedImage2.  We're finished!
            // Method 2.) Resize the originalImage and save it as ResizedImage1.  Resize ResizedImage1 and save it as ResizedImage2.  We're finished!

            // The pro to Method 1 is that we get the best possible quaility on all resized images.  The con is, this takes a long time if we're doing dozens of very large images
            // The pro to Method 2 is that it's faster than Method 1.  This is why I want to use Method 2, it's speed.  But it has a HUGE con, it's memory usage. 
            // Please run this project on an iPad connected to XCodes Instruments to monitor memory usage and see what I mean 

            zoomScale = 10f;
            resizedWidth = newWidth*zoomScale;
            resizedHeight = newHeight*zoomScale;
            UIGraphics.BeginImageContext( new SizeF( resizedWidth, resizedHeight ) );
            originalImage.Draw( new RectangleF( 0, 0, resizedWidth, resizedHeight ) );
            resizedImage1 = UIGraphics.GetImageFromCurrentImageContext();
            UIGraphics.EndImageContext();



            zoomScale = 4f;
            resizedWidth = newWidth*zoomScale;
            resizedHeight = newHeight*zoomScale;
            UIGraphics.BeginImageContext( new SizeF( resizedWidth, resizedHeight ) );

            // Run this project on an iPad and examine the memory usage in XCode's Instruments.  
            // The Real Memory Usage will be aroud 9 MB.  
            // Uncomment this "originalImage.Draw" line to see this happening, make sure to comment out the "resizedImage1.Draw" line

            // originalImage.Draw( new RectangleF( 0, 0, resizedWidth, resizedHeight ) );


            // Run this project on an iPad and examine the memory usage in XCode's Instruments.  
            // The Real Memory Usage will be aroud 55 MB!!  
            // My question is, why does the memory sky rocket when doing this, and how can I prevent the memory from sky rocketing??
            // My App requires me to resize around a hundred images and I want to be able to resize an already resized image (like in this example) without the memory usage sky rocketing like this...
            // Uncomment this "resizedImage1.Draw" line to see this happening, make sure to comment out the "originalImage.Draw" line

            resizedImage1.Draw( new RectangleF( 0, 0, resizedWidth, resizedHeight ) );
            resizedImage2 = UIGraphics.GetImageFromCurrentImageContext();
            UIGraphics.EndImageContext();


            // Add my resized images to the list of Images
            ListOfImages.Add (resizedImage1);
            ListOfImages.Add (resizedImage2);
        }
4

3 に答える 3

3

あなたのResizeコードについてはよくわかりませんが、奇妙なことをしているのを見Scaleまし。掘り下げてみると、それほど奇妙ではありませんが、明らかに明らかではありません。

の作成は、バッキングが作成されない限り、メモリに関して非常に安価になるUIImage 可能性CGImageがあります。IOW iOSは、新しいサイズに一致する新しいバッキング イメージをすぐに割り当てない場合があります。その割り当ては、必要になるまで異なります。CGImageCGImage

このような場合、一部のコード (方法 1 など) は、スケールアップ時に追加のメモリをほとんど必要としない可能性があります。ただし、2番目の方法は拡大されたイメージを使用しているCGImageため(そのためにバッキングを割り当てる必要があります)、メモリが以前に必要になります。

これをどのように確認できますか?

とを比較resizedImage.SizeしてくださいresizedImage.CGImage.Size。それらが一致しない場合は、キャッシュにヒットしている可能性があります。

ノート

  • キャッシュ ロジックが不明(文書化されていない)ため、可能性があると言いました。これは、シミュレーターやデバイスでの実行とは異なる可能性があることを知っています。また、iOS のバージョンによっても異なります。

  • キャッシングは良いことですが、驚くべきことかもしれません:-) これが文書化されていればいいのにと思います。

于 2013-03-06T18:22:10.237 に答える
0

Resizer が Dispose() メソッドを実装しているかどうかを確認しましたか? あなたがそれをどこにも処分しているのを見ません。

新しいコード行が画像全体にズームを実装しているため、メモリ使用量が増加していると思います。

Resizer は、新しく拡大されたスケールで画像全体を拡大しているため、受信した 4MB の画像が 8MB、16MB、32MB に拡大され、メモリが消費されます。

于 2013-03-06T16:41:51.877 に答える
0

UIImage実装IDisposableするので、最終的には何かが必要にDisposeなります。このResizeメソッドは への参照を「失っている」modifiedImageように見えるので、それを実行Dispose()します。呼び出し元が、処理がInitImageList_BFObjects完了したときに返されるリスト内のすべての画像に対して同じことを行っていることを願っています。または、そのクラスが実装されIDisposable、誰がそれに対処しなければならないかという問題が発生します。しかし、安心してください。作成中のこれらのイメージは、Dispose()いつかどこかに d する必要があります。

public class ImageResizer_Abridged
{
    private readonly UIImage originalImage;

    private UIImage modifiedImage;

    public ImageResizer_Abridged(UIImage image)
    {
        this.originalImage = image;
        this.modifiedImage = image;
    }

    /// <summary>
    /// stretch resize
    /// </summary>
    public void Resize(float width, float height)
    {
        UIGraphics.BeginImageContext(new SizeF(width, height));
        //
        var oldImage = this.modifiedImage;
        this.modifiedImage.Draw(new RectangleF(0, 0, width, height));
        this.modifiedImage = UIGraphics.GetImageFromCurrentImageContext();
        oldImage.Dispose();
        //
        UIGraphics.EndImageContext();
    }

    public UIImage OriginalImage
    {
        get
        {
            return this.originalImage;
        }
    }

    public UIImage ModifiedImage
    {
        get
        {
            return this.modifiedImage;
        }
    }
}
于 2013-03-06T18:43:08.303 に答える