I have several animations which, when triggered, have varying lengths of (unintentional) delays before they execute.
Inside viewDidLoad i have something like:
NSString *fileName;
myArray = [[NSMutableArray alloc] init];
for(int i = 1; i < 285; i++) {
fileName = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"HD1.2 png sequence/HD1.2_%d", i] ofType:#"png"];
UIImage *image = [UIImage imageWithContentsOfFIle:fileName];
[humptyArray addObject:image];
//NSLog(#"Added object number %d: %#", i,regularImage);
}
falling.userInteractionEnabled = NO;
falling.animationImages = humptyArray;
falling.animationDuration = 6.3f;
falling.animationRepeatCount = 1;
I put in the NSLog so i could confirm that the array is populated with the images, which it is. When i want to trigger the animation i call [falling startAniamting]. Although the array has been preloaded with images there is still a delay between me triggering the animation and the animation executing.
What can i do so that there is no delay when i trigger the animation?
#autoreleasepool {
NSString *fileName;
humptyArray = [[NSMutableArray alloc] init];
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^{
for(int i = 1; i < 285; i++) {
fileName = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"HD1.2 png sequence/HD1.2_%d", i] ofType:#"png"];
UIImage *image = [UIImage imageWithContentsOfFIle:fileName];
[humptyArray addObject:image];
//NSLog(#"Added object number %d: %#", i,regularImage);
}
falling.userInteractionEnabled = NO;
falling.animationImages = humptyArray;
falling.animationDuration = 6.3f;
falling.animationRepeatCount = 1;
dispatch_async(dispatch_get_main_queue(), ^{
[humptyArray release];
});
});
}
Taken from my own answer in this other [SO question][1]
[1]: http://stackoverflow.com/a/11364856/253008
You will find that people on SO suggest using animationImages, but it can eat up all your system memory and it is not fast. Please have a look at my answer to smooth-video-looping-in-ios, it is talking about looping video but the issue is the same. Please also take a look at my answer to iphone-smooth-transition-from-one-video-to-another. There are 2 example xcode projects available at these links that show an implementation that will start playing quickly and loop without any glitch all without eating up all your memory and causing an app crash.
Related
I'm creating images array for UIImageView so I can use [_myImageView startAnimating]; method. I figuree out that if I cache(preload) it like this way the animation is fluent.
But in Instruments I see, that game is deallocated but there is still too much memory allocated - I think there are some fragments of this UIGraphicsBeginContext stuff.
Am I using it right, is there some other way how to dealloc it?
Thanks in advance!
- (NSMutableArray *)generateCachedImageArrayWithFilename:(NSString *)filename extension:(NSString *)extension andImageCount:(int)count
{
_imagesArray = [[NSMutableArray alloc] init];
_fileExtension = extension;
_animationImageName = filename;
_imageCount = count;
for (int i = 0; i < count; i++)
{
NSString *tempImageNames = [NSString stringWithFormat:#"%#%i", filename, i];
NSString *imagePath = [[NSBundle mainBundle] pathForResource:tempImageNames ofType:extension];
UIImage *frameImage = [self loadRetinaImageIfAvailable:imagePath];
UIGraphicsBeginImageContext(frameImage.size);
CGRect rect = CGRectMake(0, 0, frameImage.size.width, frameImage.size.height);
[frameImage drawInRect:rect];
UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[_imagesArray addObject:renderedImage];
if (_isDoublingFrames)
{
[_imagesArray addObject:renderedImage];
}
else if (_isTriplingFrames)
{
[_imagesArray addObject:renderedImage];
[_imagesArray addObject:renderedImage];
}
NSLog(#"filename = %#", filename);
}
return _imagesArray;
}
- (UIImage *)loadRetinaImageIfAvailable:(NSString *)path
{
NSString *retinaPath = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:[NSString stringWithFormat:#"%##2x.%#", [[path lastPathComponent] stringByDeletingPathExtension], [path pathExtension]]];
if ([UIScreen mainScreen].scale == 2.0 && [[NSFileManager defaultManager] fileExistsAtPath:retinaPath] == YES)
return [[UIImage alloc] initWithCGImage:[[UIImage imageWithData:[NSData dataWithContentsOfFile:retinaPath]] CGImage] scale:2.0 orientation:UIImageOrientationUp];
else
return [UIImage imageWithContentsOfFile:path];
}
-(void) dealloc
{
[_imagesArray removeAllObjects];
_imagesArray = nil;
}
I had memory issues similar you wrote and what I would try out in your code
1) use #autoreleasepool
to wrap either the method or the content of the loop in #autoreleasepool { } to create a local pool which will be released after method is done. In your situation it seems to me wrapping the loop is better:
for (int i = 0; i < count; i++)
{
#autoreleasepool {
// the loop code
...
}}
2) kill [UIImage imageNamed]
Using imageNamed will prepare a cache (depending on the size of image) which will never be flushed out of the memory unless you reach a memory warning. Even if you set myImage.image = nil, the image is still in memory. XCode's Intruments tool Allocations view will show it nicely. What you can do is initialize UIImage like this, and iOS won't cache the image:
image = [UIImage imageWithContentsOfFile:fullpath];
You might use imageNamed in your code under loadRetinaImageIfAvailable.
Keep both two approach and let's check this out again!
In my app, I use UIImageView and UIScrollView to show a lot of images (every time there are about 20 images and every image is about 600px*500px and size is about 600kb).
I use this code to accomplish this function:
// Here is pictures Data;
self.klpArry = self.pictureData;
CGSize size = self.klpScrollView1.frame.size;
for (int i=0; i < [klpArr count]; i++) {
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake((size.width * i)+300, 20, 546, 546)];
NSString *filePath = [[NSBundle mainBundle] pathForResource:[klpArr objectAtIndex:i] ofType:#"jpg"];
UIImage *imageData = [[UIImage alloc]initWithData:[NSData dataWithContentsOfFile:filePath]];
[iv setImage:imageData];
iv.backgroundColor = [UIColor grayColor];
[self.klpScrollView1 addSubview:iv];
imageData = nil;
iv = nil;
iv.image = nil;
filePath = nil;
[imageData release];
[filePath release];
[iv release];
}
// show the picture in scrollview;
[self.klpScrollView1 setContentSize:CGSizeMake(size.width * numImage, size.height)];
self.klpScrollView1.pagingEnabled = YES;
self.klpScrollView1.showsHorizontalScrollIndicator = NO;
self.klpScrollView1.backgroundColor = [UIColor grayColor];
But every time I initialize this function, the memory will increase about 5MB. Actually I release UIImageView, UIimage and UIScrollView (vi.image=nil, [vi release]) but it doesn't work, the allocated memory is not getting released. BTW, I used my friend's code first vi.image = nil then vi = nil; but the pictures are not getting displayed on scrollview.
The main problem, as I see it, is that you're setting your local variables to nil and then you proceed to try to use those local variables in methods like release and the like, but because they've been set to nil, those methods now do nothing, and the objects with the +1 retainCount (or now +2 because you've added them to your view) are never released.
Thus, I'd suggest the following:
//Here is pictures Data;
self.klpArry = self.pictureData;
CGSize size = self.klpScrollView1.frame.size;
for (int i=0; i < [klpArr count]; i++) {
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake((size.width * i)+300, 20, 546, 546)];
NSString *filePath = [[NSBundle mainBundle] pathForResource:[klpArr objectAtIndex:i] ofType:#"jpg"];
UIImage *imageData = [[UIImage alloc]initWithData:[NSData dataWithContentsOfFile:filePath]];
[iv setImage:imageData];
iv.backgroundColor = [UIColor grayColor];
[self.klpScrollView1 addSubview:iv];
// Don't set these to nil, or else subsequent release statements do nothing!
// These statements are actually not necessary because they refer to local
// variables so you don't need to worry about dangling pointers. Make sure
// you're not confusing the purpose of setting a pointer to nil in ARC to
// what you're doing in your non-ARC code.
//
// imageData = nil;
// iv = nil;
// You don't want to set this to nil because if iv is not nil, your image
// will be removed from your imageview. So, not only is this not needed,
// but it's undesired.
//
// iv.image = nil;
// This statement is not necessary for the same reason you don't do it
// to imageData or iv, either. This is probably even worse, though, because
// filePath is not a variable that you initialized via alloc. You should
// only be releasing things you created with alloc (or new, copy, mutableCopy,
// for which you issued a retain statement).
//
// filePath = nil;
[imageData release];
// filePath is a +0 retainCount already, so don't release. You only release
// those items for which you increased retainCount (e.g. via new, copy,
// mutableCopy, or alloc or anything you manually retained).
//
// [filePath release];
[iv release];
}
// show the picture in scrollview;
[self.klpScrollView1 setContentSize:CGSizeMake(size.width * numImage, size.height)];
self.klpScrollView1.pagingEnabled = YES;
self.klpScrollView1.showsHorizontalScrollIndicator = NO;
self.klpScrollView1.backgroundColor = [UIColor grayColor];
Thus, your code would be simplified (and corrected) to probably just be:
//Here is pictures Data;
self.klpArry = self.pictureData;
CGSize size = self.klpScrollView1.frame.size;
for (int i=0; i < [klpArr count]; i++) {
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake((size.width * i)+300, 20, 546, 546)];
NSString *filePath = [[NSBundle mainBundle] pathForResource:[klpArr objectAtIndex:i] ofType:#"jpg"];
UIImage *imageData = [[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:filePath]];
[iv setImage:imageData];
iv.backgroundColor = [UIColor grayColor];
[self.klpScrollView1 addSubview:iv];
[imageData release];
[iv release];
}
// show the picture in scrollview;
[self.klpScrollView1 setContentSize:CGSizeMake(size.width * numImage, size.height)];
self.klpScrollView1.pagingEnabled = YES;
self.klpScrollView1.showsHorizontalScrollIndicator = NO;
self.klpScrollView1.backgroundColor = [UIColor grayColor];
Or you could further simplify your code through the use of autorelease:
//Here is pictures Data;
self.klpArry = self.pictureData;
CGSize size = self.klpScrollView1.frame.size;
for (int i=0; i < [klpArr count]; i++) {
UIImageView *iv = [[[UIImageView alloc] initWithFrame:CGRectMake((size.width * i)+300, 20, 546, 546)] autorelease];
NSString *filePath = [[NSBundle mainBundle] pathForResource:[klpArr objectAtIndex:i] ofType:#"jpg"];
UIImage *imageData = [[[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:filePath]] autorelease];
[iv setImage:imageData];
iv.backgroundColor = [UIColor grayColor];
[self.klpScrollView1 addSubview:iv];
}
// show the picture in scrollview;
[self.klpScrollView1 setContentSize:CGSizeMake(size.width * numImage, size.height)];
self.klpScrollView1.pagingEnabled = YES;
self.klpScrollView1.showsHorizontalScrollIndicator = NO;
self.klpScrollView1.backgroundColor = [UIColor grayColor];
By the way, the statement (with autorelease):
UIImage *imageData = [[[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:filePath]] autorelease];
could probably be simplified to:
UIImage *imageData = [UIImage imageWithContentsOfFile:filePath];
This gives you a UIImage, with a +0 retainCount (i.e. you don't have to release it) from your file.
So, a few final observations:
You really should probably review and study the Advanced Memory Management Programming Guide. It's dense reading if you're new to memory management, but mastery of these concepts (especially in non-ARC code) is critical.
If you haven't, I'd encourage you to run your code through the static analyzer ("Product" - "Analyze" or shift+command+B). I'd be surprised if many (if not most) of these issues wouldn't have been identified for you by the analyzer. You should have zero warnings when you run your code through the analyzer.
If you want to take this to the next level, you might want to be far more conservative about your memory management. The governing principle would be a system design that only loads the images that are needed at the UI at any given time, which involves not only calvinBhai's excellent suggestion of lazy loading of images (i.e. don't load images into memory until your UI really needs them), but also a pro-active releasing images once they've scrolled off the screen. Maybe you don't need to worry about it in your app, because you're only dealing with 20 images at a time, but if any of your portfolios/galleries expanded to 100 or 1000 images, the idea of keeping all of those in memory at any given time is just a bad idea. This is a more advanced concept, so maybe you should focus on the basic memory management problems of your existing code first, but longer term, you might want to contemplate lazy loading and pro-active releasing of images.
If memory is your concern,
try lazy loading the images = Load the visible image, the next and previous image. You dont have to have all images added to your klpscrollview.
Once you figure out lazy loading the images onto your scrollview, then you can think of fixing the images not showing on your scrollview.
Easier would be to search for "lazy load uiimage uiscrollview"
I have an animation of a menu background that is 21 frames. I load them into memory using the below code in the view's viewDidLoad method.
NSMutableArray *menuanimationImages = [[NSMutableArray alloc] init];
for( int aniCount = 0; aniCount < 21; aniCount++ )
{
NSString *fileLocation = [[NSBundle mainBundle] pathForResource: [NSString stringWithFormat: #"bg%i", aniCount + 1] ofType: #"png"];
NSData *imageData = [NSData dataWithContentsOfFile: fileLocation];
[menuanimationImages addObject: [UIImage imageWithData:imageData]];
}
settingsBackground.animationImages = menuanimationImages;
Unfortunately, doing [settingsBackground startAnimating]; doesn't work in the viewDidLoad method. Is there some way to preload the animation so there isn't a 1-3 second delay on first run?
I wouldn't normally recommend using imageNamed and relying on the in-built caching mechanisms. You'll find a lot of discussion on this if you search, but also it won't necessarily pre-render your images anyway.
I use the following code to pre-load and pre-render images so there is no delay when animating the first time through.
NSMutableArray *menuanimationImages = [[NSMutableArray alloc] init];
for (int aniCount = 1; aniCount < 21; aniCount++) {
NSString *fileLocation = [[NSBundle mainBundle] pathForResource: [NSString stringWithFormat: #"bg%i", aniCount + 1] ofType: #"png"];
// here is the code to pre-render the image
UIImage *frameImage = [UIImage imageWithContentsOfFile: fileLocation];
UIGraphicsBeginImageContext(frameImage.size);
CGRect rect = CGRectMake(0, 0, frameImage.size.width, frameImage.size.height);
[frameImage drawInRect:rect];
UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[menuanimationImages addObject:renderedImage];
}
settingsBackground.animationImages = menuanimationImages;
The problem with each of these answers is that the suggested "fix" is to preload all your image data into memory. That can and will result in crashes if your images are too big or there are too many images, see my answer for uiimage-animation-causing-app-to-crash-memory-leaks for more detailed info. The real fix is that you need to decompress the images but not hold them all in memory. Or, you can use a movie format that already does the decompression for all frames in one step, so that changing from frame to frame is not a costly operation.
The animationImages property of UIImageView requires an array of UIImage objects, not an array of NSData objects.
You may also want to set the duration and repeatCount, even though they have default values.
Example:
if (!self.idleView.animationImages) {
self.idleView.animationImages = [NSArray arrayWithObjects:[UIImage imageNamed:#"idle1.png"],
[UIImage imageNamed:#"idle2.png"],
[UIImage imageNamed:#"idle3.png"],
[UIImage imageNamed:#"idle4.png"],
[UIImage imageNamed:#"idle5.png"],
[UIImage imageNamed:#"idle6.png"],
[UIImage imageNamed:#"idle7.png"],
[UIImage imageNamed:#"idle8.png"],
[UIImage imageNamed:#"idle9.png"],
[UIImage imageNamed:#"idle10.png"],
[UIImage imageNamed:#"idle11.png"],
[UIImage imageNamed:#"idle12.png"],
[UIImage imageNamed:#"idle13.png"],
[UIImage imageNamed:#"idle14.png"],
[UIImage imageNamed:#"idle15.png"],
[UIImage imageNamed:#"idle16.png"],
[UIImage imageNamed:#"idle17.png"],
[UIImage imageNamed:#"idle18.png"],
[UIImage imageNamed:#"idle19.png"],
[UIImage imageNamed:#"idle20.png"]
, nil];
}
self.idleView.animationRepeatCount = 0;
self.idleView.animationDuration = 2.0;
[self.view addSubview:self.idleView];
[self.view sendSubviewToBack:self.idleView];
[self.idleView startAnimating];
Try the following code, normally you should have no delays. Also this code is simpler :
NSMutableArray *menuanimationImages = [[NSMutableArray alloc] initWithCapacity:21];
NSString *imageName;
for( int aniCount = 1; aniCount < 21; aniCount++ )
{
imageName = [NSString stringWithFormat:#"bg%d.png", aniCount];
[menuanimationImages addObject:[UIImage imageNamed:imageName]];
}
settingsBackground.animationImages = menuanimationImages;
// all frames will execute in 2 seconds
settingsBackground.animationDuration = 2.0;
// repeat the annimation forever
settingsBackground.animationRepeatCount = 0;
// start animating
[settingsBackground startAnimating];
It's a simple app that takes 30 pics from my Resources folder, puts them into an NSMutableArray during viewDidLoad, and then changes the pic based on accelerometer data. I have a UIImageView from IB covering the whole view, and when the accelerometer updates its info, it swaps the uiimageview.image to a diff pic. Works for about 10 seconds, and then crashes with this:
Program received signal: “0”.
Data Formatters temporarily unavailable, will re-try after a 'continue'. (Unknown error loading shared library "/Developer/usr/lib/libXcodeDebuggerSupport.dylib")
After looking around on here it sounds like it's a memory leak. Here's the code I'm using:
Interface:
{
UIAccelerometer *accel;
IBOutlet UIImageView *pic;
NSMutableArray *picArr;
}
Implementation:
- (void)viewDidLoad
{
[super viewDidLoad];
picArr = [[NSMutableArray alloc] init]; //array of pics to be used
NSString *path = [[NSString alloc] init]; //dynamic path of images to save to array
for (int i = 1; i <= 30; i++)
{
path = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"nativity_test_%i", i] ofType:#"jpg"];
[picArr addObject:[[UIImage alloc] initWithContentsOfFile:path]];
}
accel = [UIAccelerometer sharedAccelerometer];
accel.delegate = self;
accel.updateInterval = 0.5f;
[path release];
pic.image = [picArr objectAtIndex:15];
}
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
if(acceleration.y < 0.0f) //goes thru the tilted left pics
{
for (int i = 0; i <= 15; i++)
{
if( acceleration.y >= (-(15.0f-(double)i)/30.0f) && acceleration.y < (-(14.0f-(double)i)/30.0f) )
{
pic.image = [picArr objectAtIndex:i+1];
}
}
}
else if(acceleration.y > 0.0f) //goes thru tilted right pics
{
for (int i = 1; i <= 14; i++)
if( acceleration.y > ((double)i/30.0f) && acceleration.y <= ((double)(i+1)/30.0f) )
{
pic.image = [picArr objectAtIndex:i+15];
}
}
}
Please let me know if I left out any pertinent info.
This is not a crash caused by a memory leak, but by an overrelease of memory. Your crash is caused by this line:
[path release];
Remove that line and you should be fine. For some values of fine, anyway; most of what George says also applies.
The reason is that the path being released will be this one:
path = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"nativity_test_%i", i] ofType:#"jpg"];
That's an object with an effective retain count of zero. While it has a positive retain count, it's already been autoreleased and will be released when you return to the main event loop.
You should also remove this line:
NSString *path = [[NSString alloc] init]; //dynamic path of images to save to array
The reason is you're assigning the result of pathForResource to path, never actually using it. You'll need to declare path as a variable, of course. I suggest changing the pathForResource line to this:
NSString *path = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"nativity_test_%i", i] ofType:#"jpg"];
This is a smart change, as there's no reason you'd need this outside of the loop.
You should also fix this line:
[picArr addObject:[[UIImage alloc] initWithContentsOfFile:path]];
[UIImage alloc] returns an UIImage with an effective retain count of 1. Adding it to an array will increase that to two. If you're just releasing the array later, this will cause a memory leak. Instead:
[picArr addObject:[[[UIImage alloc] initWithContentsOfFile:path]] autorelease];
That will mean you're adding an UIImage with an effective retain count of 0 to the array, which will bump it to 1, making the array the only owner of the object. Later when the array goes away, so will most of the images. (The imageView will also own whichever is assigned to it.)
Don't be frustrated by this stuff: one day soon you'll just suddenly get it and wonder why it ever caused you headaches. :)
There's a couple of problems here:
1) You aren't releasing the UIImage objects you're adding to "picArr".
This line:
[picArr addObject:[[UIImage alloc] initWithContentsOfFile:path]];
Should be something like this instead:
UIImage *image = [[UIImage alloc] initWithContentsOfFile:path];
[picArr addObject: image];
[image release];
I believe you could also do:
[picArr addObject: [[[UIImage alloc] initWithContentsOfFile:path] autorelease]];
2) You don't need to do NSString *path = [[NSString alloc] init]; You can just do:
NSString *path;
You are giving the path variable a value inside that first loop so there's no need to alloc+init. Once you change this you can get rid of this line:
[path release];
3) I don't see where you are releasing "picArr" itself, you'll need to. You may be doing it already but didn't include that bit of code in your post.
What's the most efficient way to create a thumbnail from an arbitrary web image in iPhone?
the comparison of two method for fast creating thumbnail from image
kindly see this link for detail
http://www.cocoaintheshell.com/2011/01/uiimage-scaling-imageio/
or
http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/
for future , just copy pasting code from first one
First method, using UIKit
void)buildGallery
{
for (NSUInteger i = 0; i < kMaxPictures; i++)
{
NSInteger imgTag = i + 1;
NYXPictureView* v = [[NYXPictureView alloc] initWithFrame:(CGRect){.origin.x = x, .origin.y = y, .size = _thumbSize}];
NSString* imgPath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"%d", imgTag] ofType:#"jpg"];
UIImage* fullImage = [[UIImage alloc] initWithContentsOfFile:imgPath];
[v setImage:[fullImage imageScaledToFitSize:_thumbSize]];
[fullImage release];
}
Results of the benches gave me the following :
Time Profiler : 4233ms
Live bytes : 695Kb
Overall bytes used : 78.96Mb
Second method, using ImageIO
-(void)buildGallery
{
for (NSUInteger i = 0; i < kMaxPictures; i++)
{
NSInteger imgTag = i + 1;
NYXPictureView* v = [[NYXPictureView alloc] initWithFrame:(CGRect){.origin.x = x, .origin.y = y, .size = _thumbSize}];
NSString* imgPath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"%d", imgTag] ofType:#"jpg"];
CGImageSourceRef src = CGImageSourceCreateWithURL((CFURLRef)[NSURL fileURLWithPath:imgPath], NULL);
CFDictionaryRef options = (CFDictionaryRef)[[NSDictionary alloc] initWithObjectsAndKeys:(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform, (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageIfAbsent, (id)[NSNumber numberWithDouble:_maxSize], (id)kCGImageSourceThumbnailMaxPixelSize, nil];
CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(src, 0, options); // Create scaled image
CFRelease(options);
CFRelease(src);
UIImage* img = [[UIImage alloc] initWithCGImage:thumbnail];
[v setImage:img];
[img release];
CGImageRelease(thumbnail);
}
he benches gave me this :
Time Profiler : 3433ms
Live bytes : 681Kb
Overall bytes used : 77.63Mb
You can see that using ImageIO is about 19% faster than UIKit, and also uses slightly less memory.
This is best way that I've found.
The only way that I've found so far that works on any images is to show it in an ImageView with the right size, and then create a bitmap from that view.
Far from efficient, though.