Change the animation images of a UIImageView - iphone

In my viewDidLoad method of my iPhone app I have the following code:
zombie[i].animationImages = zombieImages;
zombie[i].animationDuration = 0.8/zombieSpeed[i];
zombie[i].animationRepeatCount = -1;
[zombie[i] startAnimating];
Later on in the app the following code is called:
[zombie[i] stopAnimating];
zombie[i] = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"zh.png"]];
zombie[i].animationImages = flyingZombieImages;
zombie[i].animationDuration = 0.8/zombieSpeed[i];
zombie[i].animationRepeatCount = -1;
[zombie[i] startAnimating];
This causes the app to crash, with EXC_BAD_ACCESS on the line zombie[i].animationImages = flyingZombieImages;
flyingZombieImages is initialized with the following code: (zombieImages is initialized the same way)
NSMutableArray *flyingZombieImages = [NSMutableArray array];
for (NSUInteger i=1; i <= 29; i++) {
NSString *imageName = [NSString stringWithFormat:#"flzom%d.png", i];
[flyingZombieImages addObject:[UIImage imageNamed:imageName]];
}
Why is this happening? Is there a workaround?

As Dima mentioned flyingZombieImages is likely not initialized properly, which is causing the crash. However, there is another problem as well when you create the new instances of UIImageView:
zombie[i] = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"zh.png"]];
At this point you already have a reference to the old UIImageView stored in this variable. You are losing the reference to it and most likely will leak its memory. You also would want to remove the old UIImageView from the view hierarchy and add the new one.
A better way is instead to use the original UIImageView and change its image by replacing this line with:
zombie[i].image = [UIImage imageNamed:#"zh.png"];

Related

UIImageView memory leak crash issue - Objective C

I am trying to build up my app with the images thumbnails which I loaded from server. The images are in large number. So when I try to run in my iPod it crashes after loading some 50 to 60 images. And the crash is because of the memory leak that I came to know by testing my app with instruments. I have used imageViews and a button for each imageView, and I also released those objects. Here is my code that I have used in my app.
NSInteger result = [self loadedBlipCount] + (_feed.hasMore ? 1 : 0);
if (result == 0 && _emptyFeedCell)
result = 1;
int outer_count = result/3;
scroll_view.backgroundColor = [UIColor whiteColor];
scroll_view.delegate = self;
for (int i=0; i<outer_count; i++)
//if (i<outer_count)
{
xPos = 10;
for (int j=0; j<3; j++) {
_blipyy = [_feed blipAtIndex:count];
image_view = [[URLImageView alloc] initWithFrame:CGRectMake(xPos, yPos, 90, 90)];
image_view.tag = count_tag;
image_view.url = _blipyy.image_tab_images;
UIButton *img_butt = [[UIButton alloc] initWithFrame:CGRectMake(xPos, yPos, 90, 90)];
img_butt.tag = count_tag + 10000;
[img_butt addTarget:self action:#selector(image_tapped:) forControlEvents:UIControlEventTouchUpInside];
xPos = xPos + 95;
count = count + 1;
count_tag = count_tag + 1;
//count_1= count_1 +1;
[scroll_view addSubview:image_view];
[scroll_view addSubview:img_butt];
[image_view release];
[img_butt release];
//[image_view release];
}
// });
yPos = yPos + 95;
}
Please help me with this issue. Thanks in Advance!!
Instead of scroll View take UITableView and customize UITableViewCell with your own implementation and re use cells. It will work with out any memory issue.
Try SDWebImage - it's works perfect for all my projects and so easy to integrate and use!
https://github.com/rs/SDWebImage
This problem needs you to change your approach. It's important for you to use either UITableView(It'll help you handling memory issues). However, if you still want to go ahead and use UIImageView then I'd suggest you to use this AsyncImageView. To call this API:
ASyncImage *img_EventImag = alloc with frame;
NSURL *url = yourPhotoPath;
[img_EventImage loadImageFromURL:photoPath];
[self.view addSubView:img_EventImage]; // In your case you'll add in your TableViewCell.
It's same as using UIImageView. Easy and it does most of the things for you. AsyncImageView includes both a simple category on UIImageView for loading and displaying images asynchronously on iOS so that they do not lock up the UI, and a UIImageView subclass for more advanced features. AsyncImageView works with URLs so it can be used with either local or remote files.
Loaded/downloaded images are cached in memory and are automatically cleaned up in the event of a memory warning. The AsyncImageView operates independently of the UIImage cache, but by default any images located in the root of the application bundle will be stored in the UIImage cache instead, avoiding any duplication of cached images.
The library can also be used to load and cache images independently of a UIImageView as it provides direct access to the underlying loading and caching classes.

how to load few images from URL individually to scroll view ? objective-c

i have this scroll view, that I'm loading into him few images from URL.
the problem is that the scroll view don't show any of them until that all loaded.
i want to show every image the moment i finished loading her.
my code looks like this:
-(void)loadPhotosToLeftscroll{
for (int count = 0 ; count < [leftPhotoArray count]; count++) {
NSLog(#"nextPhotoHight: %f",nextLeftPhotoHight);
NSMutableDictionary *photoDict;
photoDict = [leftPhotoArray objectAtIndex:count];
float photoHight = [[photoDict objectForKey:#"photos_height"]floatValue];
float photoWide= [[photoDict objectForKey:#"photos_width"]floatValue];
NSString *photoPath = [photoDict objectForKey:#"photos_path"];
NSData * imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString:photoPath]];
UIImage *image = [[UIImage alloc] initWithData:imageData];
UIImageView *photoView = [[UIImageView alloc]initWithFrame:CGRectMake(10 , nextLeftPhotoHight, photoWide, photoHight )];
[photoView setImage:image];
[photoView.layer setMasksToBounds:YES];
[photoView.layer setCornerRadius:6];
nextLeftPhotoHight = photoView.frame.size.height + photoView.frame.origin.y + 10;
[leftBlockScroll addSubview:photoView];
}
}
You better use a asynchronous way to do that.
Relative topic is NSURLConnection, UIImageView.
I have done something similar before.
1. Create a new model inherit to UIView
2. This model will have a UIImageView, NSData
3. When u init the model, pass in a URL
4. Use NSURLConnection to send out AsynchroizedRequest
5. By using NSURLConnection delegate, you will finally get the Data of the image
6. Init a UIImage with these data
7. Init The UIImageView with this Image
8. Add this imageview to this model or directly pointing this model to the imageview
Feel free to ask for more detail :)

Object type changes

I'm having a bit of an issue with holding a mixture of a custom class and UIImage views in an array. These are stored in the array and I'm using:
if ([[fixtures objectAtIndex:index] isKindOfClass:[Fixture class]])
to distinguish between if it's a UIIMage or Fixture object. My source code for this is:
- (void) moveActionGestureRecognizerStateChanged: (UIGestureRecognizer *) recognizer
{
switch ( recognizer.state )
{
case UIGestureRecognizerStateBegan:
{
NSUInteger index = [fixtureGrid indexForItemAtPoint: [recognizer locationInView: fixtureGrid]];
emptyCellIndex = index; // we'll put an empty cell here now
// find the cell at the current point and copy it into our main view, applying some transforms
AQGridViewCell * sourceCell = [fixtureGrid cellForItemAtIndex: index];
CGRect frame = [self.view convertRect: sourceCell.frame fromView: fixtureGrid];
dragCell = [[FixtureCell alloc] initWithFrame: frame reuseIdentifier: #""];
if ([[fixtures objectAtIndex:index] isKindOfClass:[Fixture class]]) {
Fixture *newFixture = [[Fixture alloc] init];
newFixture = [fixtures objectAtIndex:index];
dragCell.icon = [UIImage imageNamed:newFixture.fixtureStringPath];
[newFixture release];
} else {
dragCell.icon = [fixtures objectAtIndex: index];
}
[self.view addSubview: dragCell];
}
}
However, when dragging the cell that was an object of class Fixture, I would get errors such as EXC_BAD_ACCESS or unrecognized selector sent to instance (which makes sense as it was sending a CALayerArray a scale command.
I therefore set a breakpoint to see inside the fixtures array. Here I saw that the UIImages were all set to the right class type but there was also:
(CALayerArray *)
(Fixture *)
(NSObject *)
for the positions were the Fixture classes were being held in the array. Could anyone shed some light onto why it's doing this? If you need any more info to help please feel free to ask.
Denis
In your code here:
Fixture *newFixture = [[Fixture alloc] init];
newFixture = [fixtures objectAtIndex:index];
dragCell.icon = [UIImage imageNamed:newFixture.fixtureStringPath];
[newFixture release];
It looks like you're releasing an autorelease object (newFixture). When you get an object out of the array, it's autorelease.
You also have a memory leak, when you allocate the newFixture at the first line, that object is never released because you replace the pointer to it in your 2nd line.
Fixture *newFixture = [[Fixture alloc] init]; // THIS OBJECT IS NEVER RELEASED
newFixture = [fixtures objectAtIndex:index]; // YOU'RE REPLACING THE newFixture POINTER WITH AN OBJECT FROM THE ARRAY
dragCell.icon = [UIImage imageNamed:newFixture.fixtureStringPath];
[newFixture release]; // YOU'RE RELEASING AN AUTORELEASED OBJECT
So the code should be like
Fixture *newFixture = [fixtures objectAtIndex:index];
dragCell.icon = [UIImage imageNamed:newFixture.fixtureStringPath];
Then your property should retain the image correctly.

ImageIO initImageJPEG instances getting allocated and never released

Im developing an app for an iPhone and I found that the following code is causing the memory allocation to increment.
-(UIImage *)createRecipeCardImage:(Process *)objectTBD atIndex:(int)indx
{
[objectTBD retain];
// bringing the image for the background
UIImage *rCard = [UIImage imageNamed:#"card_bg.png"];
CGRect frame = CGRectMake(00.0f, 80.0f, 330.0f, 330.0f);
// creating he UIImage view to contain the recipe's data
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = rCard;
[rCard release];
imageView.userInteractionEnabled = YES;
float titleLabelWidth = 150.0;
float leftGutter = 5.0;
float titleYPos = 25.0;
float space = 3.0;
float leftYPos = 0;
// locating Title label
float currentHeight = [self calculateHeightOfTextFromWidth:objectTBD.Title :titleFont :titleLabelWidth :UILineBreakModeWordWrap];
UILabel *cardTitle = [[UILabel alloc]initWithFrame:CGRectMake(leftGutter, titleYPos, titleLabelWidth, currentHeight)];
cardTitle.lineBreakMode = UILineBreakModeWordWrap;
cardTitle.numberOfLines = 0;
cardTitle.font = titleFont;
cardTitle.text = objectTBD.Title;
cardTitle.backgroundColor = [UIColor clearColor];
[imageView addSubview:cardTitle];
[cardTitle release];
leftYPos = titleYPos + currentHeight + space;
// locating brown line
UIView *brownLine = [[UIView alloc] initWithFrame:CGRectMake(5.0, leftYPos, 150.0, 2.0)];
brownLine.backgroundColor = [UIColor colorWithRed:0.647 green:0.341 blue:0.122 alpha:1.0];
[imageView addSubview:brownLine];
[brownLine release];
leftYPos = leftYPos + 2 + space + space + space;
// creating the imageView to place the image
UIImageView *processPhoto = [[UIImageView alloc] initWithFrame:CGRectMake(leftGutter, leftYPos, 150, 150)];
if((uniqueIndex == indx) && (uniqueImg.imageData != nil))
{
if([uniqueImg.rcpIden isEqualToString:objectTBD.iden])
{
objectTBD.imageData = [NSString stringWithFormat:#"%#", uniqueImg.imageData];
[recipesFound replaceObjectAtIndex:indx withObject:objectTBD];
NSData * imageData = [NSData dataFromBase64String:objectTBD.imageData];
UIImage *rcpImage = [[UIImage alloc] initWithData:imageData];
[imageData release];
processPhoto.image = rcpImage;
[rcpImage release];
}
}
else if(objectTBD.imageData != nil)
{
NSData * imageData = [NSData dataFromBase64String:objectTBD.imageData];
UIImage *rcpImage = [[UIImage alloc] initWithData:imageData];
processPhoto.image = rcpImage;
[rcpImage release];
[decodedBigImageDataPointers addObject:imageData];
}
else
{
UIImage * rcpImage = [UIImage imageNamed:#"default_recipe_img.png"];
processPhoto.image = rcpImage;
[rcpImage release];
}
NSlog(#" Process Photo Retain Count %i", [processPhoto retainCount]); // this prints a 1
[imageView addSubview:processPhoto];
NSlog(#" Process Photo Retain Count %i", [processPhoto retainCount]); // this prints a 2!!!!
//[processPhoto release]; // this line causes an error :(
// converting the UIImageView into a UIImage
UIGraphicsBeginImageContext(imageView.bounds.size);
[imageView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[objectTBD release];
for(UIView *eachSubview in imageView.subviews)
{
[eachSubview removeFromSuperview];
NSLog(#"each subview retainCount %i despues", [eachSubview retainCount]);
// here I find that the processPhoto view has a retain count of 2 (all other views have their retain count in 1)
}
return viewImage;
}
When I checked at the instruments object allocation I found that the "GeneralBlock-9216" growing up.
Breaking down the row I found that every time I call this code, one instance of:
2 0x5083800 00:18.534 ImageIO initImageJPEG
is being allocated. Checking the call stack, the following line is highlighted:
UIImage * objImage = [UIImage imageWithData:imageData];
Any help to find what the error is?
As TechZen said, the imageWithXXX: methods cache the image inside of them while you run the program (though you release the instances after using). I recommend initWithXXX: and release API sets instead of imageWithXXX:.
Well, if you embed several debug log on your source code, check how many times is it called, and check the retain count of the instances.
As far as I can explain, that is all.
I hope you will solve the problem.
Does anyone have an answer for this? It's tearing me apart trying to figure out why this image information keeps lingering. I've tried every solution.
The situation:
Images get downloaded and stored to the device, then loaded with imageWithContentsOfFile (or even initWithContentsOfFile, which doesn't help either). When the view goes away, the images don't, but they don't show up as leaks, they're just this initImageJPEG Malloc 9.00 KB that never goes away and keeps ramping up.
UPDATE: I believe I've figured this out: Check to make sure everything is actually getting dealloc'd when you're releasing whatever the parents (and/or grandparents) and etc of the images are. If the parents don't get deallocated, they never let go of their children images, and whatever data was in those images sticks around. So check retain counts of parent objects and make sure that everything's going away all the way up whenever you release the view at the top.
A good way to check for this is to put NSLogs into custom classes' dealloc methods. If they never show up, that object isn't going away, even though the reference to it might, and it (and whatever its subviews and properties are) will never ever disappear. In the case of images, this means a pretty sizable allocation every time that object is generated and never deallocated. It might not show up in leaks, especially if the parent of the topmost object you're thinking you're releasing but actually aren't persists and doesn't itself ever deallocate.
I hope this helps. It'll be useful to take some time to read through your code with a fine-toothed comb to make sure you're allocating and releasing things properly. (Search for "alloc]", start at the top of the file, and work your way down to make sure you're releasing and that the release isn't inside of some if() or something.)
Also, running "Build and Analyze" might lock up your machine for a bit, but its results can be really helpful.
Good luck!
I think you're seeing UIImage cacheing images. There used there used to be a method something like initWithData:cache that let you turn the cacheing off. Now I think the system always caches the images automatically behind the scenes even after you've deallocted the specific instances.
I don't think its an error on your part. I think it's the system keeping data around in the OpenGl subsystem. Unless it causes a major leak, I don't think it is a problem.

How to reuse a UILabel? (Or any object)

This:
UILable *myLabel = [[UILabel alloc] init];
UILable *myLabel = [[UILabel alloc] init];
gives me a redefinition error.
But this:
for(i=0;i<5;i++)
{
UILable *myLabel = [[UILabel alloc] init];
// some label code here
[self.view addSubview:myLabel];
[myLabel release];
}
doesn't. So is the second one false? Should I define it before and just reuse it?
Is that right:
UIIMageView *Sign;
//Some Sign Stuff
Sign = [[UIImageView alloc]init];
Sign.image = [UIImage imageNamed:#"Minus.png"];
frame = CGRectMake(160 ,80, 64, 64);
Sign.frame = frame;
[scrollView addSubview:Sign];
Sign = nil;
[Sign release];
//Some other Sign stuff
Sign = [[UIImageView alloc]init];
Sign.image = [UIImage imageNamed:#"Plus.png"];
frame = CGRectMake(200 ,80, 64, 64);
Sign.frame = frame;
[scrollView addSubview:Sign];
Sign = nil;
[Sign release];
is that correct? That doesnt work without the Sign = nil. So it seems a little wobbly too.
You cannot have identical variable names used in the same block level scope. So in your first example you cannot have a variable definition with the same name, you have to name them differently.
- (void) method {
UIImageView* image1;
// here we define a new block scope. This can be a block of any kind (while, for, if)
{
// All reference in this block to this variable will see this definition.
UIImageView* image1;
// Using image1 here
}
// Here we see again the image1 defined at the beginning of the method.
}
In your loop example you are in a new scope that it's reinitialize after each iteration.
Your third example is correct in that it define the variable only one time. You reuse this variable after that to assign a new object. The third one is less elegant in that your variable name does not describe well for each case what are their purpose.
For your case of 'Sign = nil' this effectively make the line that follows useless since in Objective-C a message sent to a nil object is ignored.
I would suggest to define a method that you can call to create your images that look the same. Something like:
- (void) createImage:(NSString*) image frame:(CGRect) frame {
UIImageView *Sign;
Sign = [[UIImageView alloc]init];
Sign.image = [UIImage imageNamed:image];
Sign.frame = frame;
[self.scrollView addSubview:Sign];
[Sign release];
}
Your for-loop is perfectly fine. The scope of myLabel is limited to one run of your for-loop. So each run a new variable to hold the reference to your UILabel is created.
The second code you posted has leaks.
Sign = nil
[Sign release]
This will release the object at address nil and not the object you created. I can't see what else is wrong with your code, but your fix is definitely not fixing the root cause. Maybe it will help to post what error/warning you get when removing Sign = nil.
Also note that starting your variable names with a capital letter is not a good naming convention, because usually class names start with one.