2013-06-24 77 views
0

當遊戲第一次運行時,我需要生成並保存320個圖像作爲PNG。這些圖像將被加載而不是再次生成。下面是該過程:首次啓動時生成並存儲許多圖像iOS

  • 負載圖像模板(黑色和白色的阿爾法)
  • 覆蓋非與指定的顏色的透明像素
  • 放在上面以0.3的不透明度它合併到一個最終圖像模板
  • 返回回來的UIImage
  • 店的UIImage,在緩存目錄轉換爲NSData的爲PNG

這是使用UIGraph完成icsBeginImageContextWithOptions。此過程需要在後臺線程上的10種顏色中的32個圖像模板完成。目的是在遊戲中將這些圖像用作頭像/頭像,並在適當的時候在某些屏幕上縮小。但它們不能每次都生成,因爲這會導致太多滯後。

圖像是400x400每個。它們的結果是每個存儲時大約20/25 kB。當我嘗試使用我目前的生成和存儲方式時,我收到一條內存警告,並且看到(使用儀器)CGImage和UIImage對象的數量不斷增加。這似乎是他們被保留,但我沒有任何參考。

這裏是我的其他問題更密切的細節我正在使用的代碼:UIGraphicsBeginImageContext created image

什麼是創建並存儲到二級存儲這麼多圖像最好方式是什麼?提前致謝。

編輯:

這裏是整個代碼我目前使用它來創建並保存圖像:

//========================================================== 
// 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)來說明問題:

Project Files

供將來參考,第Ë溶液是這個代碼的一行:

tempImageView.image = nil; 

由於Matic的。

+0

你沒有張貼任何代碼,但考慮到你寫的可能是與自動釋放池一個問題:如果使用「爲」循環,你應該在循環內創建內部自動釋放池,並在每個循環中排空它以確保內部釋放這些對象。 –

+0

我嘗試在@autorelease {}中包裝for循環的內部,但它沒有幫助。有什麼我需要補充來排除它嗎?我認爲這是自動的。謝謝 – Lukas

+0

我通常分配自動釋放池,然後耗盡它,但我認爲這應該做同樣的事情。它是自動的,但它不會簡單地制動你的代碼,並釋放推到自動釋放池的對象,除非你指定。看起來有些東西是保留圖像無論如何..如果可能(並沒有其他作品),嘗試繼承UIImage並重寫保留和釋放方法,以查看哪些對象保留它們。你也使用ARC?是否有可能看到一些相關的代碼? –

回答

0

這似乎是問題在方法verticallyFlipImage。圖形上下文似乎保留了您創建的臨時圖像視圖,並保留了您分配的圖像。這個問題可能通常會通過將每個圖像作爲自己的調度呼叫來推送:Resample image - > callback - > resample next(或exit)。

在整個重採樣結束時,所有的數據都被釋放,並且沒有內存泄漏。要快速解決問題,您可以在返回圖像前簡單地撥打tempImageView.image = nil;。圖像視圖本身仍然會產生內存膨脹,但它太小而不會產生任何影響。

這適用於我,我希望它可以幫助你。

編輯:添加調度概念(註釋參考)

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]; 
     } 
    }); 
} 
+0

我試圖通過在每個自己的調度呼叫中創建每個圖像,像你建議的那樣充分優化。奇怪的是,這一切完成後,我仍剩下39張CGImages和37張UIImage。我怎麼能沒有?絕對不應該加載任何東西。 – Lukas

+0

經過大量的搞亂之後,我會與'快速修復'一起去。在自己的調度電話中製作每個圖像根本沒有任何區別。我會接受這個答案。 – Lukas

+0

我認爲你誤解了。你應該處理同一隊列中的所有圖像,但不是調用一個for循環,你應該處理第一個圖像,當它完成時得到一個回調,發送另一個圖像,完成時得到一個回調,派發另一個圖像...直到所有的重新採樣。如果您只是將每個圖像放入自己的隊列中,它會嘗試同時處理所有圖像,這會導致極度消耗內存。 –