removeFromSuperview for some reason replaces object with UIImageView? - iphone

I have been through every single thing allocated in my code and given it a tag of -1, except from the Icons, which use up the tag system. So here's my code:
NSLog(#" 1: %#", (Icon *)[self viewWithTag:index]);
Icon *icon = (Icon *)[self viewWithTag:index];
CGRect frame = icon.frame;
[icon removeFromSuperview];
icon = nil;
Icon *icon2 = [[Icon alloc] init];
[icon2 makeIconStandardWithTag:(int)index];
icon2.frame = frame;
[self addSubview:icon2];
NSLog(#" 2: %#", (Icon *)[self viewWithTag:index]);
NSLog 1 returns the object to be an icon. NSLog 2 returns the object to be a UIImageView, despite me searching my code thoroughly for every UIImageView and giving it a -1 tag. Through moving NSLog 2 around, i've discovered that the line [icon removeFromSuperview]; is the problem here. If that line isn't included, it doesn't happen. But obviously i need to remove it from superview and .alpha = 0is too much of a patch-over fix.

What value are you using for the tag? Sometimes I have troubles if I use a value that's too low (I assume because UIKit is using that tag value).
Try setting index to some random large number.
Also, why not just use an instance variable to refer to the Icon? Then you won't have to mess around with identifying the icon by its tag.

Related

Displaying CGRect position reset my image position

I'm making some CGRect Utils class and function to help us manipulating CGRect and View's frame.
I'm facing a very strange problem. I have an image, that I can move by pressing some button.
- (IBAction)heightPlusTen:(id)sender {
CGRectAddHeightToView(myView, 10);
}
CGRectAddHeightToView is just a #define and it can be replace by:
- (IBAction)heightPlusTen:(id)sender {
myView.frame = CGRectMake( myView.frame.origin.x,
myView.frame.origin.y,
myView.frame.size.width,
myView.frame.size.height+10 );
}
So far the image is moving. Everything looks fine...
Until I run this code:
- (IBAction)updatePosition:(id)sender {
xLabel.text = [NSString stringWithFormat:#"%.0f", duck.frame.origin.x];
yLabel.text = [NSString stringWithFormat:#"%.0f", duck.frame.origin.y];
wLabel.text = [NSString stringWithFormat:#"%.0f", duck.frame.size.width];
hLabel.text = [NSString stringWithFormat:#"%.0f", duck.frame.size.height];
}
It's suppose to display the x,y,width and height on the screen. But instead it move back the image to it almost initial position.
I know lot of code is missing so checkout CGRect Utils on github.
It's autolayout that's causing the problem.. Now you can just delete auto-layout all together, or you can keep it (along with its many benefits) but also insantiate the labels as soon as the xib loads.. like so:
// ViewController.m
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
xLabel.text = [NSString stringWithFormat:#"%.0f", duck.frame.origin.x];
yLabel.text = [NSString stringWithFormat:#"%.0f", duck.frame.origin.y];
wLabel.text = [NSString stringWithFormat:#"%.0f", duck.frame.size.width];
hLabel.text = [NSString stringWithFormat:#"%.0f", duck.frame.size.height];
}
that way autolayout won't have to readjust anything when you update the labels in the future.
You really should consider using ARC in this day and age.
As for your question: autolayout is causing this frame reset when you update the labels.
If you disable it on ViewController.xib then your code works "as expected".
A very good tutorial on autolayout and how to use it can be found here: Beginning Auto Layout in iOS 6
Autolayout is a great tool but can sometimes cause serious headaches.

Memory Leak Warnings, Due To NSArray Of Large Images

I have an iPad app, which is children's book. You navigate from one page to the next by calling this action, which essentialy fades out the current page and fades in the next page, then fills the three stack UIImageViews with the next row of the NSArray, so it's prepared to complete the action for the next page:
-(void)delayedMethod{
[leftButton setAlpha:1];
[pageImageNext setAlpha:0];
[pageImage setAlpha:1];
[pageImageBack setAlpha:1];
NSString *imageExtension = #".jpg";
NSString *audioExtension = #".wav";
if (activePage == thePages.count/3)
{
activePage = 1;
}
else
{
activePage = activePage + 1;
}
NSInteger row = 0;
if(activePage == 0)
{
row = activePage;
}
else
{
row = ((activePage) * 3);
}
[leftButton setAlpha:1];
pageImageBack.image = [UIImage imageNamed:[NSString stringWithFormat:#"%#%#", [thePages objectAtIndex:row+0], imageExtension]];
pageImage.image = [UIImage imageNamed:[NSString stringWithFormat:#"%#%#", [thePages objectAtIndex:row+1], imageExtension]];
pageImageNext.image = [UIImage imageNamed:[NSString stringWithFormat:#"%#%#", [thePages objectAtIndex:row+2], imageExtension]];
[UIImageView beginAnimations:NULL context:NULL];
[UIImageView setAnimationDuration:.5]; // you can set this to whatever you like
/* put animations to be executed here, for example: */
[leftButton setAlpha:0];
[rightButton setAlpha:0];
[leftButton setAlpha:1];
[rightButton setAlpha:1];
leftButton.hidden = NO;
/* end animations to be executed */
[UIImageView commitAnimations]; // execute the animations listed above
[imageExtension release];
}
The NSArray is loaded in this action, which is called in the ViewDidLoad:
-(void)loadPages
{
thePages = [[NSArray alloc] initWithObjects:
// How many stacked images do we need to get fade in and outs? Also add column fo
#"page0",#"page0",#"page1",
#"page0",#"page1",#"page2",
#"page1",#"page2",#"page3",
#"page2",#"page3",#"page4",
#"page3",#"page4",#"page5",
#"page4",#"page5",#"page6",
#"page5",#"page6",#"page7",
#"page6",#"page7",#"page8",
#"page7",#"page8",#"page9",
#"page8",#"page9",#"page10",
#"page9",#"page10",#"page11",
#"page10",#"page11",#"page12",
#"page11",#"page12",#"page13",
#"page12",#"page13",#"page14",
#"page13",#"page14",#"page15",
#"page14",#"page15",#"page16",
#"page15",#"page16",#"page17",
#"page16",#"page17",#"page18",
#"page17",#"page18",#"page19",
#"page18",#"page19",#"page20",
#"page19",#"page20",#"page21",
#"page20",#"page21",#"page22",
#"page21",#"page22",#"page23",
#"page22",#"page23",#"page24",
#"page23",#"page24",#"page25",
#"page24",#"page25",#"page26",
#"page25",#"page26",#"page27",
#"page26",#"page27",#"page27",
nil];
}
It all works just fine until about the 10-12th page when I start to get memory warnings in the console and usually a crash.
I am pretty sure it is just a matter of releasing the three large UIImageViews at the right time, but I can't figure out when...I've tried a number of different spots in the code.
I was doing an app like this, where I had a lot of image views and labels that the user could change. I got warnings and crashes all the time. The only thing that seemed to fix it all was to make them all properties. Everything I wanted to hang on to on a certain view I had to make a property. That took care of it nicely, otherwise it seemed like the OS freaked out that I had to much allocated, and would send memory warnings which would release stuff, and then when the app tried to access them it would crash. See if making your pageImageNext, pageImage , pageImageBack properties works.
Make sure you are doing a [thePages release]]; in the dealloc method of the view controller. Not releasing that will definitely cause memory issues.
Also have you made the imageviews retained properties in the view controller? If so, you need to release them in the dealloc as well.
As for memory warnings, you can unset the imageviews in didRecieveMemoryWarning and then just add in a check to re-create them as needed.
The code:
[imageExtension release];
Is incorrect and should be deleted. "imageExtension" is allocated on the stack, and will go away when the method exits. Only release things that you have alloc'd.
Do you have just one of these types of ViewControllers? Or one for each page?
#sasquatch #YuzaKen #MishieMoo If I simply set self.pageImage = nil; in the dealloc, as you suggested, won't that only release the memory when a user leaves the ViewController?
I feel like I need to be releasing the UIImageViews as I move down the rows of the array, when I replace the contents of the UIImageView with a new image, no?

How do i properly discard a subview?

I have a problem with my app where the code for which is far too long to go into, but suffice to say when i'm removing a UIView and replacing it with a new one like so:
NSLog(#" .. %#", (Icon *)[self viewWithTag:index]);
Icon *icon = (Icon *)[self viewWithTag:index];
CGRect frame = icon.frame;
int tag = icon.tag;
[icon removeFromSuperview];
[icon release];
Icon *icon2 = [[Icon alloc] init];
icon2.frame = frame;
[icon2 makeIconStandardWithTag:(int)tag];
[self addSubview:icon2];
It does some weird thing where that NSLog the first time (because the view is already there) shows that the object is an icon, but the second time after running this code shows that it's a UIImageView for some reason now, and it displays what i presume to be the original icon at some odd position on the screen. It's very erratic behaviour. But what i do know is this:
Removing the [icon removeFromSuperview]; line, although keeping the object there, stops this behaviour and causes the NSLog to return an Icon, as it should.
So my guess is that it's not removing icon correctly. Is there a way to completely remove icon, or is removeFromSuperview as far as i can go. What i could do is just have it set to alpha = 0 but this is more of a patch-over solution and not how i want to solve it.
"Is there a way to completely remove
icon, or is removeFromSuperview as far
as i can go"
You can set the object to nil:
icon = nil;
Can you verify what "self" is in this line of code:
It might not be what you think.
[self addSubview:icon2];
NSLog(#" Self is %#", self);
This is a guess, but try setting self.tag to -1 or some other value that doesn't collide with the tags you're setting on your Icon objects. The viewWithTag: method searches the current view and its subviews for a match, so if self.tag == 0 and you call [self viewWithTag:0], you'll get self.
Did you retain icon somewhere prior to this? If not, no need to release it after the call to removeFromSuperview. Similarly, unless you need the reference to icon2 elsewhere, you can release that after calling addSubview.
Views retain views added via addSubview, and they release views removed via removeFromSuperview.

How can I see the intermediate updates on my UI while debugging iPad code?

I am developing an app in which I am using a plist file. There are 21 key-value pairs in the plist. Each pair is a dictionary (type) with 6 items. The dictionary contains a set of images. In my program, I am using the path to retrieve the images. My requirement is that the images should be displayed one-by-one on the imageView. i have done it successfully. The images are being dispalyed exactly from plist.
So my question is can I use the debugger to see the intermediate execution of the plist? When I placed breakpoints in my code, and run using the debugger, I am able to step into the code and the images are displayed on the view only after the whole execution of plist with 21 key-value pairs is done. How can I see the images on the view while debugging each pair?
(void)setSequenceInfo:(NSDictionary *)sequenceInfo
{
[self.subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];
self.sequenceQueue = [NSMutableArray array];
//load the sequenceinfo dictionary
[_sequenceInfo release];
if (!sequenceInfo)
return;
_sequenceInfo = [sequenceInfo retain];
//create one UIImageView by sequence
NSMutableDictionary* views = [NSMutableDictionary dictionaryWithCapacity:[sequenceInfo count]];
for (NSString* identifier in _sequenceInfo)
{
UIImageView* seqView = [[UIImageView alloc] initWithFrame:self.bounds];
seqView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
seqView.contentMode = self.contentMode;
//the image is hidden until its sequence is played
seqView.hidden=YES;
//add the newly created image view to our subviews
[self addSubview:seqView];
//also store it in our sequenceViews dictionary
[views setValue:seqView forKey:identifier];
[seqView release];
}
self.sequenceViews = views;
}
In order to "see" intermediate results while using the debugger, you'll have to change the structure of your code to return to the main run loop, so that the UIImageView has the chance to run and display the new image. You could set up a timer that fires periodically, and in the timer routine you change the UIImageView's image to the next one in the sequence. Then you set your breakpoint in the timer handler method in gdb, and when you hit that breakpoint each time you should see the previously displayed image.
EDIT: here's some more detail, based on what I think you want to do. Realize, though, that I think there's no "magic wand" to be able to see changes on the screen while you're in the debugger. You have to return to the Run Loop so that your UIKIt widgets get cycles to draw themselves.
For example, say you have this method:
- (void) updateTwoLabels:(NSString *)newText
{
self.label1.text = newText;
self.label2.text = newText; // put breakpoint on this line
}
and you want to stop in the debugger just before label2's text is set, and see that label1 has changed. Well, I think there's no way of doing that, except by modifying your code, and calling yourself again with performSelector:withObject:afterDelay: so that the main run loop can run for a while then call your code again:
BOOL callAgain = NO;
BOOL setLabel2 = YES;
- (void) updateTwoLabels:(NSString *)newText
{
self.label1.text = newText;
if ( setLabel2 )
self.label2.text = newText; // put breakpoint on this line
if ( callAgain )
[self performSelector:#selector(updateTwoLabels:) withObject:newText afterDelay:0];
}
in this way, when you're debugging, you can set callAgain to YES and setLabel2 to NO in gdbg, and keep "continuing" until you see that label1 has changed. That's the basic technique.
Do the whole method in a background thread but call GUI stuff on the main thread. For example: [self performSelectorOnMainThread:#selector(addSubview:) withObject:seqView waitUntilDone:YES]. Don't forget to make an autorelease pool at the beginning of the method (the one that runs in the background thread) and drain it at the end of it.

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.