0

ゲームを最初に実行するときに、320 の画像を生成して PNG として保存する必要があります。これらの画像は、再度生成される代わりに読み込まれます。プロセスは次のとおりです。

  • 画像テンプレートの読み込み (アルファ付きの白黒)
  • 指定された色で透明でないピクセルをオーバーレイする
  • テンプレートの上に 0.3 の不透明度で配置し、それを 1 つの最終的な画像にマージします
  • UIImage を返す
  • NSData に変換された UIImage を Cache ディレクトリに PNG に保存します

これは、UIGraphicsBeginImageContextWithOptions を使用して行われます。このプロセスは、バックグラウンド スレッドの10 色の 32 個の画像テンプレートに対して実行する必要があります。目的は、これらをこのゲームのアバター/プロフィール画像として使用し、特定の画面で適切に縮小することです。ただし、これはラグが大きすぎるため、毎回生成することはできません。

画像はそれぞれ 400x400 です。保存すると、それぞれ約 20/25 kB になります。現在の生成方法と保存方法を使用しようとすると、メモリ警告が表示され、(Instruments を使用して) 生きている CGImage および UIImage オブジェクトの数が急速に増加し続けていることがわかります。これは保持されているようですが、私はそれらへの言及を保持していません。

これが、私が使用しているコードを詳しく説明する私の他の質問です: UIGraphicsBeginImageContext で作成された画像

この多くの画像を作成してセカンダリ ストレージに保存する最善の方法は何ですか? 前もって感謝します。

編集:

画像を作成して保存するために現在使用しているコード全体を次に示します。

//==========================================================
// Definitions and Macros
//==========================================================

//HEX color macro
#define UIColorFromRGB(rgbValue) [UIColor \
colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \
green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \
blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]

//Colours
#define RED_COLOUR UIColorFromRGB(0xF65D58)
#define ORANGE_COLOUR UIColorFromRGB(0xFF8D16)
#define YELLOW_COLOUR UIColorFromRGB(0xFFD100)
#define LIGHT_GREEN_COLOUR UIColorFromRGB(0x82DE13)
#define DARK_GREEN_COLOUR UIColorFromRGB(0x67B74F)
#define TURQUOISE_COLOUR UIColorFromRGB(0x32ADA6)
#define LIGHT_BLUE_COLOUR UIColorFromRGB(0x11C9FF)
#define DARK_BLUE_COLOUR UIColorFromRGB(0x2E97F5)
#define PURPLE_COLOUR UIColorFromRGB(0x8F73FD)
#define PINK_COLOUR UIColorFromRGB(0xF35991)



#import "ViewController.h"

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    //Generate the graphics
    [self generateAndSaveGraphics];

}




//==========================================================
// Generating and Saving Graphics
//==========================================================

-(void)generateAndSaveGraphics {

    dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        [self createAvatarImages];

        //Here create all other images that need to be saved to Cache directory


        dispatch_async( dispatch_get_main_queue(), ^{ //Finished

            NSLog(@"DONE"); //always runs out of memory before getting here
        });

    });
}

-(void)createAvatarImages {

    //Create avatar images
    NSArray *colours = [NSArray arrayWithObjects:RED_COLOUR, ORANGE_COLOUR, YELLOW_COLOUR, LIGHT_GREEN_COLOUR, DARK_GREEN_COLOUR, TURQUOISE_COLOUR, LIGHT_BLUE_COLOUR, DARK_BLUE_COLOUR, PURPLE_COLOUR, PINK_COLOUR, nil];

    NSString *cacheDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];


    for(int i = 0; i < 32; i++) { //Avatar image templates are named m1 - m16 and f1 - f16

        NSString *avatarImageName;

        if(i < 16) { //female avatars

            avatarImageName = [NSString stringWithFormat:@"f%i", i+1];
        }
        else { //male avatars

            avatarImageName = [NSString stringWithFormat:@"m%i", i-15];
        }


        for(int j = 0; j < colours.count; j++) { //make avatar image for each colour

            @autoreleasepool { //only helps very slightly

                UIColor *colour = [colours objectAtIndex:j];
                UIImage *avatarImage = [self tintedImageFromImage:[UIImage imageNamed:avatarImageName] colour:colour intensity:0.3];

                NSString *fileName = [NSString stringWithFormat:@"%@_%i.png", avatarImageName, j];
                NSString *filePath = [cacheDir stringByAppendingPathComponent:fileName];

                NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(avatarImage)];
                [imageData writeToFile:filePath atomically:YES];

                NSLog(@"AVATAR IMAGE CREATED");

            }

        }
    }
}




//==========================================================
// Universal Image Tinting Code
//==========================================================

//Creates a tinted image based on the source greyscale image and tinting intensity
-(UIImage *)tintedImageFromImage:(UIImage *)sourceImage colour:(UIColor *)color intensity:(float)intensity {

    if (UIGraphicsBeginImageContextWithOptions != NULL) {

        UIGraphicsBeginImageContextWithOptions(sourceImage.size, NO, 0.0);

    } else {

        UIGraphicsBeginImageContext(sourceImage.size);
    }

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGRect rect = CGRectMake(0, 0, sourceImage.size.width, sourceImage.size.height);

    // draw alpha-mask
    CGContextSetBlendMode(context, kCGBlendModeNormal);
    CGContextDrawImage(context, rect, sourceImage.CGImage);

    // draw tint color, preserving alpha values of original image
    CGContextSetBlendMode(context, kCGBlendModeSourceIn);
    [color setFill];
    CGContextFillRect(context, rect);


    //Set the original greyscale template as the overlay of the new image
    sourceImage = [self verticallyFlipImage:sourceImage];
    [sourceImage drawInRect:CGRectMake(0,0, sourceImage.size.width,sourceImage.size.height) blendMode:kCGBlendModeMultiply alpha:intensity];

    UIImage *colouredImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    colouredImage = [self verticallyFlipImage:colouredImage];


    return colouredImage;
}

//Vertically flips an image
-(UIImage *)verticallyFlipImage:(UIImage *)originalImage {

    UIImageView *tempImageView = [[UIImageView alloc] initWithImage:originalImage];

    UIGraphicsBeginImageContext(tempImageView.frame.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, tempImageView.frame.size.height);

    CGContextConcatCTM(context, flipVertical);

    [tempImageView.layer renderInContext:context];

    UIImage *flippedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();



    return flippedImage;
}

@end

問題を説明するために、テスト プロジェクト (zip 内) を作成しました。

プロジェクト ファイル

今後の参考のために、解決策は次の 1 行のコードです。

tempImageView.image = nil;

マティックのおかげです。

4

1 に答える 1

0

問題は method にあるようですverticallyFlipImage。グラフィックスコンテキストは、作成した一時的な画像ビューと、割り当てた画像を保持しているようです。この問題は、通常、各画像を独自のディスパッチ呼び出しとしてプロセスにプッシュすることで修正される可能性があります: Resample image -> callback -> resample next (または exit)。

リサンプリング全体の最後に、すべてのデータが解放され、メモリ リークは発生しません。簡単な修正を行うtempImageView.image = nil;には、画像を返す前に呼び出すだけです。画像ビュー自体は依然としてメモリの膨張を生成しますが、影響を与えるには小さすぎます。

これは私にとってはうまくいきます。

EDIT:ディスパッチの概念を追加しました(コメント参照)

dispatch_queue_t internalQueue;
- (void)createQueue {
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void) {
        internalQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL); //we created a high priority queue
    });
}
- (void)deleteQueue {
    dispatch_release(internalQueue);
}
- (void)imageProcessingDone {
    [self deleteQueue];
    //all done here
}
- (void)processImagesInArray:(NSMutableArray *)imageArray {
    //take out 1 of the objects (last in this case, you can do objectAtIndex:0 if you wish)
    UIImage *img = [[imageArray lastObject] retain]; //note, image retained so the next line does not deallocate it (released at NOTE1)
    [imageArray removeLastObject]; //remove from the array
    dispatch_async(internalQueue, ^(void) { //dispach

        //do all the image processing + saving

        [img release];//NOTE1

        //callback: In this case I push it the main thread. There should be little difference if you simply dispach it again on the internalQueue
        if(imageArray.count > 0) {
            [self performSelectorOnMainThread:@selector(processImagesInArray:) withObject:imageArray waitUntilDone:NO];
        }
        else {
            [self performSelectorOnMainThread:@selector(imageProcessingDone) withObject:nil waitUntilDone:NO];
        }
    });
}
于 2013-06-24T14:02:05.710 に答える