What's the correct way of creating an UILabel and releasing it again when I want to re-use this until i'm out of records in my array?
I wanted to this this:
// create label
UILabel *labelIWishToRecycle;
for (int i = 0; i < [myArrayFullOfItems count]; i++) {
// Edit the label
labelIWishToRecycle = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 640)];
}
// Release label
[labelIWishToRecycle release];
When I do it this way I get a crash for releasing it. So I just don't release it. Works like a charm now. Of course, this is not the way, so I was wondering what IS.
Should I create and release the label for every item in the array? Or should I create it outside the for-loop but release it within? Or ...?
Thanks in advance.
For what are you using the label!? If you are adding it to a view, than you have to use a new instance everytime.. if you only do size calculations or stuff like that, than you could use the same label over and over again.
But the trick is to initialize your local variable:
UILabel *labelIWishToRecycle = nil;
If you have done this, it is save to send release on it (also if no real label has been assigned yet). Before, your pointer is pointing on a random address and you are trying to release the object at that address. That will be a crash in the most cases.
(Guessing the problem case is the one, when your array count is zero.)
Related
I have this UITableView which is displaying images, downloaded rom a database, as a matrix: 4 images in each table row.
To be able to select images from the views I'm using a UITapGestureRecognizer. To make each selection unique I've been trying to tag each tap recognizer and each imageView. That's where the problem is...
I've put a log within the for-loop that is creating and tagging the imageViews and recognizers and I can see in the output that they pass through all the values. However when I try to get the tag by later pressing an image I always get "3" (the last number in the table row). This makes me think the tags are simlpy overwriting eachother even though I'm creating a new object in each loop. Either that or I'm reading it out the wrong way.
Unrelated parts cut out.
for (NSInteger i = 0; i < 4; i++){
asyncImage = [[AsyncImageView alloc]
initWithFrame:frame];
[asyncImage loadImageFromURL:url];
asyncImage.tag = i;
NSLog(#"TAG %d", asyncImage.tag);
tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap)];
tapRecognizer.view.tag = i;
NSLog(#"TapTAG %d", asyncImage.tag);
[asyncImage addGestureRecognizer:tapRecognizer];
}
And the method:
- (void)handleTap{
NSLog(#"TAP %d", self.tapRecognizer.view.tag);
}
If you think I'm doing it all totally wrong, a light push in the right direction is always welcome!
Thanks in advance, Tom
The following line has no effect until the gesture recognizer has been added to a view:
tapRecognizer.view.tag = i;
This is because tapRecognizer's view is initially nil. Make the assignment on the last line of your for loop to correct this problem.
Also your NSLog is always showing the tag of the last recognizer that you have added
self.tapRecognizer.view.tag // Instance variable
not the one that fired the event. Change handleTap as follows:
- (void)handleTap:(UITapGestureRecognizer*) tapRecognizer{
NSLog(#"TAP %d", tapRecognizer.view.tag);
}
You should also replace the tapRecognizer instance variable with a local variable in the method that adds the recognizer to the view, and add a colon : to your selector name:
action:#selector(handleTap:)
// HERE: ----^
I think you're doing it wrong in the loop.
Your loop is run 4 times and every time you run the loop you store AsyncImageView to the asyncImage variable (local or instance?). So the first time you run the loop, you create an object and store it at the asyncImage location, the second time this is overwritten, the third....
You have initialized 4 ImageViews, but you are only referencing to the last one. And the last one holds the correct GestureRecognizer, you want.
When do you add the ImageView to a view?
If you use the instance varable and overwrite it directly, all the other ImageViews you added to the screen point to the pointer of asyncImage. And the pointer - after running 4 times the loop and exchanging asyncImage data - points to the last image in the loop.
Hope you understand, what the problem is here.
So i have a bunch of dynamically loaded labels..
Each of them has the same name because there is no telling how many there will be..
I have another method (not the one that created the labels) changing the text for one of the labels, but when i run it only the last label that was created will change..
I need it to change the one that has a certain tag or something..
Help is much appreciated, this website is yet to let me down.
self.myLabel cannot be connected to multiple labels, so it will contain the reference of last created label, you will have to create new label every time, and you can't track them by class properties, you have to access label by their tag.
you can set tag for each label, below is sample code,
for(int i=0; i< numberOfLabels; i++)
{
UILabel *label = [[UILabel alloc] init];
label.tag = i; // do not use tag 0 here.. u can use i+1, or i+100.. something like this.
[self.view addSubview:label];
}
to access labels,
UILabel *label = (UILabel*)[self.view viewWithTag: labelTag];
Okay since you dont have any code to show i guess i have to speculate.
What i understood is that you are creating Dynamic UILabels in ur code and you want to access them. Since you have same name for all the UILabels you might me loosing the previous UILabel when every time you create a new UILabel. So in order to keep track of how many UILabel you created you must add them in an Array. Declare an NSMutableArray in your viewController.h file and make sure in the viewDidLoad u allocate it like
arrForLabels = [[NSMutableArray alloc]init];
Since it is an NSMutableArray you can add object to it.
So when u create a UILabel make sure you add the same UILabel in the Array as well
for Instance
[arrForLabels addObject:yourLabel];
you can try to NSLog your Array to see its content.
Now all youu got to do is to Create a weak link like that
UILabel *tempLabel = [arrForLabels objectAtIndex:1];
now tempLabel will be the UILabel to change text
tempLabel.text = #"My New Text";
It will work fine.
Feel free to ask for any issues in it.
I am having trouble with memory building up and am not able to empty it once I am done with it. When I look at the diagnostic tool ": Allocations: Instruments: Object summary: statistics", the memory is just continuously building up.
example:
for (int i=0; i<100000; i++){
UILabel *lblPost = [[UILabel alloc] initWithFrame:CGRectMake(x,y,w,d)];
[lblPost setText: "Hello World"];
[self.view addSubview: lblPost];
// tried each of the following
//[lblPost dealloc]; //neither work to clear memory it just builds
//[lblPost release]; //
}
--> Do I need to seperate CGRect out and clear that.
--> (I know I can just keep writing to one label, this is a simplified version where in the bigger version, one label would not work so easily. )
--> (I find it hard to believe that I can not create an object and then destroy it 10000 or 100000000 times over. In standard C, I can accomplish this with memory-blocks by using "free()" )
The view you are adding your label to is retaining it, that's why each none of the labels is deallocated (even if you send release message)
Maybe i really don't understand what you're trying to do, but your each indiviual object you'r allocating is retained in the view. Let me try to explain it in the code:
for (int i=0; i<10000; i++){
UILabel *lblPost = [[UILabel alloc] initWithFrame:CGRectMake(x,y,w,d)];
// lblPost now has a retain count of 1, as you alloc'd it, you'll have to release it!
[lblPost setText: "Hello World"];
[self.view addSubview: lblPost];
// lblPost now has a retain count of 2, as adding to the view adds a reference to it
[lblPost release]
// you alloc'd it, now you should release it. it now has a retain count of 1, which means it's in the ownerhsip of the self.view
}
Now, when you release or free self.view, the lblPost objects should be released as well
Why are you allocating memory for 10000 UILabels and adding them as a subview exactly? That's iPhone torture. The items in bold cause your surge in memory. Plus you're releasing none of them.
Also - never ever ever call dealloc yourself - dealloc is called automatically when you release something.
This is fun! I have decided to jump in. I posted this in my comments but here it is again:
I think madhu misunderstood the [lblPost release]. This "release" only applies to the lblPost instance. Not the ones retained by self.view... etc. So you still have 10000 label retained by self.view...
So, you create 10000 instances of lblPost and then release all of them (10000) by this line [lblPost release] in your for loop. That is just fine. But then in your for loop you also have this line [self.view addSubview: lblPost]. This line will add 10000 instances to your self.view. And they are the reason why your system crashed.
for (int i=0; i<10000; i++){
UILabel *lblPost = [[UILabel alloc] initWithFrame:CGRectMake(x,y,w,d)];
[lblPost setText: "Hello World"];
[self.view addSubview: lblPost];
[lblPost release];
//You should release after adding it to your view
}
What is the best approach to release multiple instances of an object that you can't release right away?
For example, if you are creating a bunch of thumbnails:
for(int i=0; i <totalThumbs; i++){
Thumb *newThumb = [[Thumb alloc] initWithImage:someImage]
//position the thumbs here, etc.
//assume releasing here breaks the app because we need to interact with the thumbs later
// [newThumb release] --- breaks the app
}
Would it make sense to put all the new objects in an array and release them all in viewDidUnload when we no longer need them?
Presumably you are adding each newThumb as a subview of some other view or to an array, so you should be fine to do that and then release newThumb here. For example:
Thumb *newThumb = [[Thumb alloc] initWithImage:someImage];
[myThumbs addObject:newThumb];
[newThumb release];
This works becuase myThumbs retains the object.
In order not to leak the memory, especially if you regenerate the thumbnails, you would want to iterate over the superview's subviews (all the thumbs), remove each from the superview, and release them. You may also need to do this in you dealloc method where you release the superview (assuming you do that). With an array, you could simply call removeAllObjects, I believe.
Maybe I'm missing something, but why not use autoreleasepools?
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for(int i=0; i <totalThumbs; i++){
Thumb *newThumb = [[[Thumb alloc] initWithImage:someImage]autorelease];
}
[pool drain];
Calling autorelease will add it to the pool (that you can create in any scope you like). Just call drain (or release) on the pool when you're done with it. This will release all queued objects.
You can release them right after adding to the array, because the array retains them:
for(int i=0; i <totalThumbs; i++){
Thumb *newThumb = [[Thumb alloc] initWithImage:someImage]
//position the thumbs here, etc.
[thumbsArray addObject:newThumb];
[newThumb release]; // --- doesn't break the app
}
In viewDidUnload and/or dealloc release the array. You don't need to release every single thumb.
We should always avoid allocating memory in a loop. In your case you should release memory immediately after using the object you have created. i.e
for(int i=0; i <totalThumbs; i++){
Thumb *newThumb = [[Thumb alloc] initWithImage:someImage];
//position the thumbs here, etc.
//assume releasing here breaks the app because we need to interact with the thumbs later
// [newThumb release] --- breaks the app
// Work with newThumb
[newThumb release];
}
By doing this the objects get released each time loop runs. Actually each time the loop runs, a new object is created. This is how you can manage memory allocation in a loop.
Cheers!
The Code:
// Inside my BoardsViewController.m
- (void)createImage {
imageCounter ++;
board = [[Boards alloc] init];
[self.view addSubview:board];
[board release];
board is supposed to be changed everytime, and is instead of being named board: be named 1_board, 2_board, 3_board, everytime I call this method
}
I want to have the Boards(UIView subclass) have the name of imageCounter, and also have board. Kinda like: 1_board. Meaning I want to have a Boards called something different everytime I call this method.
EDIT:
This should help maybe:
I want to have this one method that I will call multiple times allocate a Board(subclass of UIView) but have them all different names other than only one name. Meaning I increment the view counter everytime before allocating the view. So I want to have the name include the variable inside the integer: viewCounter. So that I cal call the different views seperatly and control each allocation differently.
It is not clear what do you want to achieve. If you want to distinguish between different Board instances later you can use a tag property (available in all UIView subclasses):
- (void)createImage {
imageCounter ++;
Boards *board = [[UIImageView alloc] init];
board.tag = imageCounter;
[self.view addSubview:board];
[board release]; // Note that you need this line also, you current code produces memory leak
}
Later you can get each of the created Boards using:
Boards* yourBoard = [self.view viewWithTag: someTag];
You can also define some custom identifier in your Board class if you want. Changing the name of the local variable (e.g. board to whatever_board) does not really make sense as this name will not be accessible outside of the scope of this function anyway.