NSArray code not being run - ios

I can't figure this one out, hopefully someone can help.
This is the code in my ViewController.h file.
{
NSArray *bgImages;
int currentBackground;
bool hasShrunk;
bool hasMoved;
CGAffineTransform translate;
CGAffineTransform sizeShrink;
CGAffineTransform sizeGrow;
}
Here is some of my code in the ViewController.m file.
- (void)viewDidLoad {
hasMoved = NO;
hasShrunk = NO;
currentBackground = 0;
bgImages = [[NSArray alloc] initWithObjects:
[UIImage imageNamed:#"WallPaper_01.png"],
[UIImage imageNamed:#"WallPaper_02.png"],
[UIImage imageNamed:#"WallPaper_03.png"],
[UIImage imageNamed:#"WallPaper_04.png"],
[UIImage imageNamed:#"WallPaper_05.png"],
nil];
sizeShrink = CGAffineTransformMakeScale(.25,.25);
sizeGrow = CGAffineTransformMakeScale(1,1);
_myBackground.image = [bgImages objectAtIndex:currentBackground];
}
Here is the issue. This code and the entire project run fine on the simulator, no issues. When I change the output to an actual iPhone device it fails. The code is failing on the allocation of the array called bgImages. If I place a breakpoint on viewDidLoad and step into it line by line, it executes up to the array allocation line and then jumps over it. When it then attempts to execute the line:
_myBackground.image = [bgImages objectAtIndex:currentBackground];
it will fail because the array is empty. I don't understand how this can work in the simulator but not on the actual device. The code does not look incorrect but I am at my wits end trying to understand why it behaves this way. Have I coded it incorrectly?

You're getting a problem because the array doesn't have any elements in it. One possibility is that all of the [UIImage imageNamed:...] calls are returning nil. Break one of these out as a separate variable, e.g.
UIImage *firstImage = [UIImage imageNamed:#"WallPaper_01.png"];
and set a breakpoint on that line to make sure that the image is being initialized correctly. If it isn't, one possible reason is that you have the 2x version (WallPaper_01#2x.png) but not the 1x version, or vice versa.
Edit: As #rdelmar notes, another possible reason is that, since Mac OS X uses (by default) a case-insensitive filesystem but iOS uses a case-sensitive filesystem, you're using the wrong filename, but you're only seeing a problem on iOS because OS X doesn't care that the filename isn't exactly right.

You seem to be loading all images, then just using one. This may lead to memory problems in a real device. What you may want to do is something like this;
bgImages = [[NSArray alloc] initWithObjects:
[#"WallPaper_01.png"],
[#"WallPaper_02.png"],
[#"WallPaper_03.png"],
[#"WallPaper_04.png"],
[#"WallPaper_05.png"],
nil];
...
_myBackground.image =
[UIImage imageNamed:[bgImages objectAtIndex:currentBackground]];
Also, have a look at #rdelmar's comment about case sensitivity.

Related

Objective-C: Uncast element of NSMutableArray returns unexpected type

If am having trouble understanding the following. Consider the following code:
UIImage *image = [[UIImage alloc] initWithContentsOfFile:#"car.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:imageView];
UIImage *image2 = [imageView image];
UIImage *image3 = [[array objectAtIndex:0] image];
UIImage *image4 = [(UIImageView *) [array objectAtIndex:0] image];
The instructions with image2 and image4 work as expected. The line with image3 on the other hand displays an issue,
Incompatible pointer types initializing 'UIImage *' with an expression
of type 'CIImage *'
I understand not type-casting the object retrieved from NSMutableArray might cause a problem. However, I have a hard time understanding why the compiler would think this expression to be of type CIImage.
If this question has been asked in more general terms, I apologize, I couldn't find it...
Let's pull this apart:
UIImage *image3 = [[array objectAtIndex:0] image];
Just look at this part:
[array objectAtIndex:0]
Objective-C has no type information for the object returned from this method call, so it types it as an id. Now you are asking to send the image message to an id. But an id is not a typed object. What is Objective-C to make of this?
Basically, it has to guess what message you are sending. So it looks thru its known repertory of image methods, and just picks one. It knows of six such methods:
You meant the fourth one. But it happens that the compiler picks the first one, which returns a CIImage. So by your standards, it guessed "wrong". But you made it guess, and it had to guess something.
This is exactly the problem that the cast in your last example fixes:
UIImage *image4 = [(UIImageView *) [array objectAtIndex:0] image];
So, the moral: don't do what you did in the image3 line. Do what you did in the image4 line! You have type information that the compiler doesn't have: tell the compiler what you know.
(Observe that no harm is actually done by the way you wrote the code. The compiler warning is just that: a warning. In actual fact, when the image message is sent to the UIImageView, a UIImage will be returned and all will be well. But the compiler is warning you that it doesn't know this. By not casting you have given up all static typing and forced the compiler to take its hands off. The compiler doesn't know what will happen, so it warns you that this will have to be resolved at runtime. When you cast, you resolve the matter at compile time.)

CAKeyframeAnimation - Animating an Array of Images creates a huge allocation after completion

I'm trying to animate an array of UIImage with CAKeyframeAnimation. Easy in theory.
Sample code at the bottom of the post.
My problem is that after the animation did finish, I've got a huge leak that is impossible to get rid of it.
Code to init CAKeyframeAnimation:
- (void)animateImages
{
CAKeyframeAnimation *keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:#"contents"];
keyframeAnimation.values = self.imagesArray; // array with images
keyframeAnimation.repeatCount = 1.0f;
keyframeAnimation.duration = 5.0;
keyframeAnimation.removedOnCompletion = YES;
CALayer *layer = self.animationImageView.layer;
[layer addAnimation:keyframeAnimation
forKey:#"flingAnimation"];
}
Adding a delegate to the animation and removing the animation manually cause the same leak effect:
... // Code to change
keyframeAnimation.delegate = self;
// keyframeAnimation.removedOnCompletion = YES;
keyframeAnimation.removedOnCompletion = NO;
keyframeAnimation.fillMode = kCAFillModeForwards;
....
Then:
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
if (flag)
{
[self.animationImageView.layer removeAllAnimations];
[self.animationImageView.layer removeAnimationForKey:#"flingAnimation"]; // just in case
}
}
The result is always a huge allocation. The size of the stack of memory is proportional to the size of the images:
I uploaded an example to GitHub to check the code.
SOLVED
I found the problem.
As gabbler was saying there was not a leak problem. The problem was a high allocation of Images.
I was releasing the array with the images, however, the images did not disappear from memory.
So finally I found the problem:
[UIImage imageNamed:#""];
From method definition:
This method looks in the system caches for an image object with the specified name and returns that object if it exists. If a matching image object is not already in the cache, this method locates and loads the image data from disk or asset catelog, and then returns the resulting object. You can not assume that this method is thread safe.
So, imageNamed: stores the image in a private Cache.
- The first problem is that you can not take control of the cache size.
- The second problem is that the cache did not get cleaned in time and if you are allocating a lot of images with imageNamed:, your app, probably, will crash.
SOLUTION:
Allocate images directly from Bundle:
NSString *imageName = [NSString stringWithFormat:#"imageName.png"];
NSString *path = [[NSBundle mainBundle] pathForResource:imageName
// Allocating images with imageWithContentsOfFile makes images to do not cache.
UIImage *image = [UIImage imageWithContentsOfFile:path];
Small problem:
Images in Images.xcassets get never allocated. So, move your images outside Images.xcassets to allocate directly from Bundle.
Example project with solution here.

Memory leaks when loading images to scroller

I have a scrollView in which i load images into from the net .I sometimes get memory warnings, which i assume are because i am doing something wrong with the images loader.
I am trying to fix little things, and i just wanted to show the code here, and hear maybe there are more things i can fix to get rid of this warnings.
So every time the scroller (iPad) has only 4/5 images that are : current page-3->current page+3.
This is how i load the images(every image has also a blur effect with Apple's classes) :
(should i allocated imageView every time? can i improve something here? )
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^
{
NSData *imdata2 = [NSData dataWithContentsOfURL:url];
dispatch_async(dispatch_get_main_queue(), ^
{
UIImage *theImage=[UIImage imageWithData:imdata2 scale:1];
UIImage *LightImage = [theImage applyLightEffect];
UIImage *scaledImage =[resizer resizeImageToWidth:[Globals sharedGlobals].imagesWidth WithImage:theImage];
CGRect viewSizeBack=CGRectMake(scroller.bounds.size.width*toPage , 0, scroller.bounds.size.width, scroller.bounds.size.height);
int x=[Globals sharedGlobals].pageMargins;
int y=([UIScreen mainScreen].bounds.size.height-scaledImage.size.height)/2;
CGRect viewSizeFront=CGRectMake(x , y, scaledImage.size.width,scaledImage.size.height);
UIImageView *backImageView=[[UIImageView alloc] initWithFrame:viewSizeBack];
UIImageView *frontImageView=[[UIImageView alloc] initWithFrame:viewSizeFront];
backImageView.layer.cornerRadius = 0.0;
backImageView.layer.masksToBounds = YES;
backImageView.image=LightImage;
frontImageView.layer.cornerRadius = 0.0;
frontImageView.layer.masksToBounds = YES;
frontImageView.image=scaledImage;
frontImageView.layer.borderWidth=1.0;
frontImageView.layer.borderColor=[UIColor colorWithRed:255.0 green:255.0 blue:255.0 alpha:1.0].CGColor;
[backImageView addSubview:frontImageView];
backImageView.tag=toPage;
frontImageView.tag=toPage;
[scroller addSubview:backImageView];
});
});
You should only ever have 3 images loaded at a maximum - the previous page (if it exists), the current page and the next page.
Any other images you have loaded above this is wasteful because you can't see them and they're just taking up memory for no good reason. If the images aren't too big then you can maintain them in memory and purge them when you get a warning, but for large images this will still generally cause you issues.
If you don't use ARC then add this:
[backImageView autorelease];
[frontImageView autorelease];

Animation Terminating due to Memory even with ARC and imageFilePath

I'm doing a simple animation of png's after my app loads and displays its launch screen. The animation works in the simulator but not on the actual iPhone, where it terminates due to memory pressure (the launch screen does load first though). In the debug, the Memory increases exponentially to like 200MB until it crashes. The instruments show no leaks, All Heap and anonymous Vm 9.61 MB overall. The animation doesn't show at all on the actual iPhone.
I've already stopped using imageNamed to set the animation images and used imageFilePath as suggested by another help topic. It works and the images load on the simulator. I'm just not sure what else to do. Help would be very much appreciated.
In didFinishLaunchingWithOptions:
[self.window makeKeyAndVisible];
self.animationView = [[UIImageView alloc] initWithFrame:CGRectMake(0,0, 320, 480)];
NSArray *animationArray = [[NSArray alloc] initWithObjects:
[UIImage imageFromMainBundleFile:#"/Splash_00000.png"],
[UIImage imageFromMainBundleFile:#"/Splash_00001.png"],
//There's about 150 of these so I'll omit the rest
nil];
self.animationView.animationImages = animationArray;
self.animationView.animationDuration = 5.0f;
self.animationView.animationRepeatCount = 1;
[self.animationView startAnimating];
[self.window addSubview:self.animationView];
[self.window bringSubviewToFront:self.animationView];
In case it's needed, this is the method I'm using that I got from another thread:
+(UIImage*)imageFromMainBundleFile:(NSString*)aFileName
{
NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
return [UIImage imageWithContentsOfFile:[NSString stringWithFormat:#"%#/%#", bundlePath, aFileName]];
}
Putting aside that splash screens aren't recommended, you're not leaking but you're running out of heap (which is why it works on the simulator).
Each of those images is owned by the array and won't be released by ARC until long after the animation has completed. Bare in mind that the PNGs, while compressed on disk, will be uncompressed in memory.
Solutions - there are a couple that spring to mind.
Split the animations into a sequence of discrete phases and ensure
that images are released (using #autoreleasepool) between each phase
More realistically, render the animation as a movie and play that
instead (far less likely to stutter between phases)

Apparent leaks: png_malloc

I have an application with various animations and images. The application runs just fine for about 30 minutes, but then crashes. I have looked through the instruments and I notice that there are a whole bunch of 7kB png_malloc allocations building each time I mark the heap (amounting to about 300kB every couple minutes).
I noticed in my leaks that every time an animation or png is used for the first time, there seems to be a "leak" of the data (although I am a bit skeptical whether this is a real leak or not).
All of these images have been declared using
frameName = [[NSString alloc] initWithFormat:#"image.png"];
UIImage * u = [UIImage cachelessImageNamed:frameName];
so I don't believe there should be a problem with caching the images.
Has anyone else had the same problem with this png_malloc allocation?
The instruments screenshot
*Notes: I am using arc and the animations are getting set to nil in the deallocation function; however, these isn't called until the application exits. Does this create a problem each time the animation is run if it's only been created once?
EDIT Some more code:
-(void) createSymbolAnimations
{
if (symbolAnimations == nil)
{
symbolAnimations = [[NSMutableArray alloc]init];
}
NSString * frameName;
if (thisAnimation == nil)
{
thisAnimation = [[NSMutableArray alloc] init];
}
for (int x= 0; x< 40; x++)
{
frameName = [[NSString alloc] initWithFormat:#"image%d%s",x,".png"];
UIImage * u = [UIImage cachelessImageNamed:frameName];
[thisAnimation addObject:u];
}
[symbolAnimations addObject:thisAnimation];
}
Is the creation of the animation. Imagine I have a few of these and then I change the animation set and start animating on touch with this snippet:
UIImageView * aView = [frameArray objectAtIndex:x];
aView.image = [[symbolAnimations objectAtIndex:x]objectAtIndex:0];
[aView startAnimating];
Where x is the set of images I want to animate and 0 is the first frame of the animation.
So the image is changed quite a few times and I'm starting to worry that each time the animation images are changed, the RAM isn't cleared but instead over/rewritten.
EDIT Image grabber
+(UIImage *) cachelessImageNamed: (NSString *) name
{
return [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:name ofType:nil]];
}
Just in case anyone stumbles upon this later, I found the problem.
The pngs used in this project for animations were created in Windows (not sure how pertinent that is) and it seems the file format is slightly different than the png that XCode is expecting. This disallows any png from being deallocated. If you convert the format to a png for Mac, it seems to work fine. I did this through
mogrify -type truecolormatte -format png *.png
After adjusting all of my images, the leaks were greatly reduced and everything seems to run fine.

Resources