I have the following code which adds a label into a footer of a UITableView, so that I can format the text (white, etc.)
It works ok, but it gives me a leak warning for the "headerLabel" when analyzing it on the line with the "return"
// create the parent view that will hold header Label
UIView* customView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 15.0, 300.0, 44.0)];
// create the button object
UILabel * headerLabel = [[UILabel alloc] initWithFrame:CGRectZero];
headerLabel.backgroundColor = [UIColor clearColor];
headerLabel.opaque = NO;
headerLabel.textColor = [UIColor whiteColor];
headerLabel.highlightedTextColor = [UIColor whiteColor];
headerLabel.font = [UIFont systemFontOfSize:14];
headerLabel.textAlignment=UITextAlignmentCenter;
headerLabel.frame = CGRectMake(10.0, 0.0, 300.0, 75.0);
headerLabel.numberOfLines=4;
if (section==0) {
headerLabel.text = #"If turned off, the last used settings will be used on the next session\n\n"; // i.e. array element
}
[customView addSubview:headerLabel];
//[headerLabel release];
return customView;
// [customView release];
I've tried to put the release here and there, but it's always the same.
I'd appreciate some feedback from you guys.
try
[headerLabel release];
return [customView autorelease];
You have to release headerLabel before exiting the method:
[headerView release];
You probably should autorelease customView unless your method name includes the words new, alloc or copy (in that case, the caller would have to release the returned view):
return [customView autorelease];
autorelease your customView and make sure you are releasing headerLabel after you add it as a subview. Anytime you call alloc/init you are taking ownership, you need to make sure you release those objects. Since you are returning customView from this method it makes sense to defer your release of that object (using autorelease) so it can be used by the calling object.
// create the parent view that will hold header Label
UIView* customView = [[[UIView alloc]
initWithFrame:CGRectMake(0.0, 15.0, 300.0, 44.0)]
autorelease];
// create the button object
UILabel * headerLabel = [[UILabel alloc] initWithFrame:CGRectZero];
headerLabel.backgroundColor = [UIColor clearColor];
headerLabel.opaque = NO;
headerLabel.textColor = [UIColor whiteColor];
headerLabel.highlightedTextColor = [UIColor whiteColor];
headerLabel.font = [UIFont systemFontOfSize:14];
headerLabel.textAlignment=UITextAlignmentCenter;
headerLabel.frame = CGRectMake(10.0, 0.0, 300.0, 75.0);
headerLabel.numberOfLines=4;
if (section==0) {
headerLabel.text = #"If turned off, the last used settings will be used on the next session\n\n"; // i.e. array element
}
[customView addSubview:headerLabel];
[headerLabel release];
return customView;
Based on the code sample you've got here, the first release would be correct. (Releasing after the return statement wouldn't make sense). You took ownership of the object when you created it, and you need to release it.
You can use Instruments to track where object is being retained and released; you can see a history of the leaky object to see exactly what's going on. That would be your best bet here.
Launch your app with the Leaks instrument, and when you find the leaking object, click the arrow to the right of the address. This will show you the object's history - every retain and release.
Related
I am trying to customize my tableView in my IOS app. When my tableView(or rather array) is empty, I want to display a customized label instead of the items in the tableView. The label I am referring to is "label0". But something is terribly wrong, my [label0 setHidden:YES]; or [label0 setHidden:NO]; only works in the first block of the if "method"? In the second block (if else) nothing happens no matter what I try to set the label as (hidden or shown).
What have I missed? I cannot see my own fault?
- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *headerView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, 30)] autorelease];
UILabel *label0 = [[[UILabel alloc] initWithFrame:CGRectMake(0, 25, tableView.bounds.size.width - 0, 100)] autorelease];
if ([self.searchResults count] == 0){
headerView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"lista2.png"]];
UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(5, 3, tableView.bounds.size.width - 5, 18)] autorelease];
label.text = #"Information";
label.textColor = [UIColor whiteColor];
label.backgroundColor = [UIColor clearColor];
[headerView addSubview:label];
label0.text = #"Test test test";
label0.textColor = [UIColor blackColor];
label0.backgroundColor = [UIColor whiteColor];
[tableView addSubview:label0];
[label0 setHidden:NO];
}
else {
headerView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"lista2.png"]];
UILabel *label2 = [[[UILabel alloc] initWithFrame:CGRectMake(5, 3, tableView.bounds.size.width - 5, 18)] autorelease];
label2.text = #"Search results";
label2.textColor = [UIColor whiteColor];
label2.backgroundColor = [UIColor clearColor];
[headerView addSubview:label2];
[label0 setHidden:YES];
}
return headerView;
}
EDIT
I have moved the code to viewDidLoad and set the property for the UILabel. This have unfortunately not solved my problem....
UILabel *label0 = [[[UILabel alloc] initWithFrame:CGRectMake(0, 25, tableView.bounds.size.width - 0, 100)] autorelease];
[tableView addSubview:label0];
if ([self.searchResults count] == 0){
label0.text = #"Test test test";
label0.textColor = [UIColor blackColor];
label0.backgroundColor = [UIColor whiteColor];
[label0 setHidden:NO];
}
else {
[label0 setHidden:YES];
}
This is because your label0 is created every time this method is called so in "else" you are referring to totally different object (not the one that you added to tableView when array was empty).
You shouldn't be adding subviews to tableView from this method. Consider using viewDidLoad. That way you will be adding label0 only once. To achieve that add label0 as property of your viewController.
You forgot to add label0 as subview please put this line in the else statement
[tableView addSubview:label0];
also I cannot see any benefit from doing so. I believe you can just hide the table view and show another view that has the label. but nesting views that way is not good when you come back to debug this code after 1 month you will struggle to understand it.
You say in your edit that you set the property for the UILabel (I assume you mean that you have a property called label0?). If this is so, then when you alloc init your label, it should be self.label0 = ..... not UILabel *label0 = .....
I have an application with navigation controller and some table view controller. In table view controller I have a two section header my definitions:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
if (section == 0) {
UIView* customView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, 74)];
UIImageView *myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"toolbarTopBack.png"]];
UILabel *headline = [[UILabel alloc] initWithFrame:CGRectMake(11, 14, 305, 21)];
headline.backgroundColor = [UIColor clearColor];
headline.textColor = [UIColor whiteColor];
headline.font = [UIFont fontWithName:#"Helvetica Neue" size:21];
headline.text = searchPosition;
UILabel *subHeadline = [[UILabel alloc] initWithFrame:CGRectMake(11, 36, 305, 21)];
subHeadline.backgroundColor = [UIColor clearColor];
subHeadline.textColor = [UIColor grayColor];
subHeadline.font = [UIFont fontWithName:#"Helvetica Neue" size:16];
subHeadline.text = searchRegion;
[customView addSubview:myImageView];
[customView addSubview:headline];
[customView addSubview:subHeadline];
return customView;
} else {
// create the parent view that will hold header Label
UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, 44)];
customView.userInteractionEnabled = YES;
UIImageView *myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"mainToolBar.png"]];
UIToolbar *topToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, 44)];
topToolbar.barStyle = UIBarStyleDefault;
[topToolbar setBackgroundColor:[UIColor clearColor]];
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{
NSMutableDictionary *appSettingsData = [[NSMutableDictionary alloc] initWithDictionary:[appDelegate getAppStrings]];
NSArray *segmentItems = [NSArray arrayWithObjects:[NSString stringWithFormat:#"%# (%d)", [appSettingsData valueForKey:#"segmentButton4"], listCountOffers], [NSString stringWithFormat:#"%# (%d)", [appSettingsData valueForKey:#"segmentButton5"], [[appDelegate comunication] getSimilarCount:[appDelegate getCurrentCI] idPosition:idPosition idRegion:idRegion]], nil];
segmentControl = [[UISegmentedControl alloc] initWithItems:segmentItems];
segmentControl.frame = CGRectMake(6, 8, 308, 29);
segmentControl.autoresizingMask = UIViewAutoresizingFlexibleWidth;
segmentControl.segmentedControlStyle = UISegmentedControlStyleBar;
[segmentControl setTintColor:[UIColor grayColor]];
[segmentControl addTarget:self action:#selector(segmentedControlIndexChanged:) forControlEvents:UIControlEventValueChanged];
segmentControl.momentary = NO;
segmentControl.selectedSegmentIndex = 0;
});
UIBarButtonItem *toolbarItem = [[UIBarButtonItem alloc] initWithCustomView:segmentControl];
[topToolbar addSubview:toolbarItem.customView];
[customView addSubview:myImageView];
[customView addSubview:topToolbar];
return customView;
}
}
I use "static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{", because I need create this header only first time (because when I scroll table, method is call and call ... and this is wrong) ...
Everything works fine, when I create table view controller it show a headers, when I scroll it, nothing is recreating (it is fine), but when I push back button a then reopen tableview controller headers are empty. Where is problem ? Is there any solution, how to fix it ? Thanks a lot
Well, to me it looks like it's a problem with your static dispatch.
When you push the Back button, chances are that your view holding the table view is released (I don't know your code, but I suppose it is like that), meaning all internal variables are gone - except your static dispatch, which won't be called the next time you instantiate the view. So, during the next instantiation your segmentItems will not be created, but because the view was released, they are empty. You should solve your 'only create once' problem differently, e.g. by remembering the created segmentItems in a dictionary and getting them from there if they do not exist yet.
You're using dispatch_once without really needing to. The block will only be executed once, even if you subsequently remove the view controller from memory and deallocate segmentControl.
Use lazy loading instead - create a property for your segmentControl view within your view controller, and in the accessor for that, if the backing ivar is nil, create it then:
Your synthesize statement:
#synthesize segmentControl = _segmentControl
Your accessor method:
-(UISegmentedControl*)segmentControl
{
if (_segmentControl)
return _segmentControl;
UISegmentedControl *segmentControl = //... create your control here
self.segmentControl = segmentControl
return segmentControl;
}
Then when you want to use the view, use self.segmentControl. The first time you call it, it will be created, the subsequent times, it will be re-used.
check it with breakpoint and see the process. it maybe the memory allocation problem. See this it may help you. http://www.icodeblog.com/2010/12/10/implementing-uitableview-sections-from-an-nsarray-of-nsdictionary-objects/
i am working on one application in which i have added 5 labels dynamically in a function.when i recall the same function the labels are overridden on the previously created labels in spite of releasing the labels on each creation.
for(int i = 1; i < [array count]; i++)
{
CGRect lblframe = CGRectMake(count, ycord, width, height);
UILabel *label = [[UILabel alloc] initWithFrame:lblframe];
label.backgroundColor = [UIColor blackColor];
label.font = [UIFont systemFontOfSize:17];
label.textAlignment = UITextAlignmentCenter;
label.textColor = [UIColor colorWithRed:(188/255.f) green:(149/255.f) blue:(88/255.f) alpha:1.0];;
label.text = [arr objectAtIndex:i];
count = count + xcord;
[subcatScroll addSubview:label];
[label release];
}
Write below code before for loop to get your requirement:
for (id object in [subcatScroll subviews]) {
[object removeFromSuperview];
}
I'm not sure I completely follow, so correct me if I'm misunderstanding.
Every time you call this function, you are adding a number of new labels. So if you call this function the second time, assuming 'count', 'ycord', 'width', and 'height' correspond with the values that the first call had, you are obviously adding a second group of labels in the same place as the first ones which are now directly on top of one another. You are not "overriding" the old labels, you are placing a second group directly ontop of the old ones.
Calling "release" on each label, only means you are decreasing the retainCount by 1. This number is used for memory management only. This means if you now remove the labels from the view the memory is released.
CGRect lblframe = CGRectMake(10.0, ycord, 200.0, 20.0);
UILabel *label = [[UILabel alloc] initWithFrame:lblframe];
NSLog(#"retainCount of label: %d", [label reatinCount]); // will print "1" since you called alloc
[self.view addSubview:label];
NSLog(#"retainCount of label: %d", [label reatinCount]); // will print "2" since adding to subview increases retaincount by one
[label release];
NSLog(#"retainCount of label: %d", [label reatinCount]); // will print "1" since you released
[label removeFromSuperview]; // will decrease retainCount of label to "0" and therefore free the memory
so say you wanted to remove the previously created labels from the view, you would have to do so. Either keep a reference to each of them and call "removeFromSuperview" on each of them.
If the only thing in the view where you're adding the labels, you can also just remove every subview that was added to it like so:
// remove old labels
for (UILabel *aLabel in [self.view subviews]) [aLabel removeFromSuperview];
NSArray *myArray = [NSArray arrayWithObjects:#"I", #"II", #"III", #"IV", nil];
for (int i=0; i<[myArray count]; i++) {
float ycord = i*40.0;
CGRect lblframe = CGRectMake(10.0, ycord, 200.0, 20.0);
UILabel *label = [[UILabel alloc] initWithFrame:lblframe];
label.backgroundColor = [UIColor blackColor];
label.font = [UIFont systemFontOfSize:17];
label.textAlignment = UITextAlignmentCenter;
label.textColor = [UIColor colorWithRed:(188/255.f) green:(149/255.f) blue:(88/255.f) alpha:1.0];;
label.text = [myArray objectAtIndex:i];
[self.view addSubview:label];
[label release];
}
I hope this helped, but providing further info on what you're trying to do may make it easier to help you.
I am wanting to show a simple loading dialog when certain things are happening in my app. I figured I would just create a new view, add a label to that, and then set that view to a subView of my current view.
When doing this, I don't see anything!
Here is how I am writing my method:
- (void)showLoading {
UIView *loading = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
loading.backgroundColor = [UIColor blackColor];
UILabel *txt = [[UILabel alloc] initWithFrame:CGRectMake(198, 9, 94, 27)];
txt.text = #"Loading...";
txt.textColor = [UIColor whiteColor];
[loading addSubview:txt];
[super.view addSubview:loading];
[super.view bringSubviewToFront:loading];
[loading release];
[txt release];
}
Am I doing this completely wrong?
EDIT:
I added it to the viewDidLoad method, and it works how I want:
loading = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
loading.backgroundColor = [UIColor blackColor];
UILabel *txt = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 94, 27)];
txt.text = #"Loading...";
txt.textColor = [UIColor whiteColor];
[loading addSubview:txt];
[txt release];
[self.view addSubview:loading];
[self.view bringSubviewToFront:loading];
But when loading it from a method, it seems to lag, and not show up for a bit.
Although this doesn't directly answer your question, I'd recommend grabbing MBProgressHUD from GitHub and using that in place of a static label. Looks better, less code for you to directly maintain, etc. You can find it at http://github.com/matej/MBProgressHUD
The way I use it is by creating a subclass of UITableViewController and defining a handful of methods to show and hide the HUD view. From there, I call each relevant method when I'm loading or done loading.
Specifically, I have four methods: -hudView, -showLoadingUI, -showLoadingUIWithText:, and -hideLoadingUI.
-hudView creates a new MBProgressHUD object if one doesn't already exist, and adds it to the current view ([self.view addSubview:hudView]).
-showLoadingUI calls -showLoadingUIWithText: with a default title, -showLoadingUIWithText: just unhides the MBProgressHUD and sets a label value for it (self.hudView.labelText = #"foo";).
-hideLoadingUI hides the hudView ([self.hudView hide:YES]).
First, I don't think UIView has method called init. You may just call the super of it. The appropriate method you should call is - (id)initWithFrame:(CGRect)aRect . The frame is the position, the size of the View you want to display. More here
Another thing is why you call [super.view addSubView:], I think it should be self.view, isn't it?
I have added a UILabel to my view programmatically like this:
myLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 30.0f)];
myLabel.center = CGPointMake(160.0f, 120.0f);
myLabel.backgroundColor = [UIColor clearColor];
myLabel.textColor = [UIColor whiteColor];
myLabel.font = [UIFont fontWithName:#"Helvetica" size: 18.0];
myLabel.textAlignment = UITextAlignmentCenter;
myLabel.text = #"Hello";
[self.myView addSubview:myLabel];
To add a label to my view. What I can't seem to find out is once I'm done w/ the label (at a future point) how can I delete it from the view? [myLabel release] doesn't seem to work which I think makes sense because the view it's added to probably retained it's over reference. So what is the best practice?
[myLabel removeFromSuperview];