Do IOS UI elements need to be weak? [duplicate] - ios

This question already has answers here:
Should IBOutlets be strong or weak under ARC?
(11 answers)
Closed 7 years ago.
When not using interface builder I always keep strong references to UI elements:
#interface myViewController : UIViewController
#property (nonatomic, strong) UILabel *folderLabel;
And then add them like this:
[self.view addSubview self.folderLabel];
where the initialiser is thus:
-(UILabel *)folderLabel{
if(!_folderLabel) {
_folderLabel = [[UILabel alloc] init];
_folderLabel.text = #"foo";
}
return _folderLabel
}
I have been told that this is bad for some reason and they should always be weak..
#property (nonatomic, weak) UILabel *folderLabel;
-(UILabel *)folderLabel{
if(!_folderLabel) {
UIlabel *folderLabel = [[UILabel alloc] init];
folderLabel.text = #"foo";
[self.view addSubview:folderLabel];
_folderLabel = folderLabel;
}
return _folderLabel
}
Is the strong reference a bad thing here?

When you add the subview to self.view, it gets retained. One pattern that works is to alloc the subview and store it in a local variable, add it to self.view then assign it to the ivar.

Related

Initializing from an array of property pointers

I'm trying to write a shortcut for initializing my custom UIView's properties, rather than write out each one line by line, but unfortunately it's not working the way I expected.
// .h file
#property (nonatomic, strong) UIView *view1;
#property (nonatomic, strong) UIView *view2;
#property (nonatomic, strong) UIView *view3;
#property (nonatomic, strong) UIView *view4;
// .m file
UIView *views[] = { self.view1, self.view2, self.view3, self.view4 };
for ( int i = 0; i < sizeof(views) / sizeof(views[0]); i++ ) {
views[i] = [[UIView alloc] initWithFrame:self.frame];
}
NSLog( #"%#", self.view1 ); // prints null
Is this just not allowed with C-style arrays?
Your initialization of views[] looks fine, but as soon as you do
views[i] = ...
you are merely overwriting the contents of the array. You are, however, not initializing self.view1, self.view2, etc. Try
NSLog(#"%#", views[0]);
to see what I mean.
I would not do it like follows, but you could probably do (untested):
UIView **views[] = { &self.view1, &self.view2, &self.view3, &self.view4 };
for (int i = 0; i < (sizeof(views) / sizeof(views[0]); ++i)
{
*views[i] = [[UIView alloc] initWithFrame: self.frame];
}
NSLog(#"%#", self.view1);
I would rather initialize them one by one, without the array. I don't see any advantage in using an array and a loop.
You are just changing the pointer inside the array, the self.view1 pointer will still point to nil.
You could have an array of views as property:
#property(nonatomic,strong) UIView **views;
Or maybe you can use Key Value Coding like
for(int i=1;i<5;i++){
NSString* key = [NSString stringWithFormat:#"view%i",i];
UIView *view = //initialize view
[self setValue:view forKey:key];
}
Calling e.g. self.view1 at the beginning will give you nil before you have initialized it. So the line
UIView *views[] = { self.view1, self.view2, self.view3, self.view4 };
is effectively creating an array of 4 nils.
Inserting the new instances of UIView to the array afterwards has no effect on the properties. In fact, a property is only a pair of selectors (e.g. view1 and setView1:) and by itself cannot be used reliably with C-style pointers.
You could use the backing ivars with an array of UIView ** pointers, but you're most likely better off rethinking the whole approach.

Why objects are not created using property directly?

I am working on the UIPopover and in one of the example I found that the Popover object is
created but then the object is assigned to the property of the Viewcontroller.
UIPopoverController* aPopover = [[UIPopoverController alloc] initWithContentViewController:content];
self.popoverController = aPopover;
What is the merit in such assignment and any reason for not assigning object to the property directly?
There is no "merit" in it. Saying
self.popoverController =
[[UIPopoverController alloc] initWithContentViewController:content];
would be absolutely equivalent.
On the other hand there is nothing wrong with using a temporary variable (aPopover) as shown in your example. It's just a name (a pointer); there is no significant waste of space or time. Moreover, saying self.popoverController repeatedly (either to set or to get its value) is to be avoided, because this is a method call - you are passing through the setter method or getter method (which may be synthesized, may have side effects, and does in fact take some extra time). Thus, when there is much configuration to be done (for example), it is best to do it as shown in your example:
UIPopoverController* aPopover =
[[UIPopoverController alloc] initWithContentViewController:content];
// lots of configuration here, using the local automatic variable aPopover...
// ...and then, only when it is all done, call the setter:
self.popoverController = aPopover;
The only reason for that is that you probably read this in a tutorial somewhere. And the author did it for readability for beginners. You could absolutely use:
self.popoverController = [[UIPopoverController alloc] initWithContentViewController:content];
All depending on how familiar you are with programming in general and how readable you want your code to be.
I agree with the others that, in this case, the assigning of the popover controller to a local variable before later assigning it to a class property is largely a stylistic matter. But this is only the case because you are keeping a strong reference to that popover controller. There are other situations where you have weak properties, in which this local variable pattern is critical.
For example, let's assume that we have a bunch of controls that we're going to add to our view controller's view programmatically:
#property (nonatomic, strong) UIView *containerView;
#property (nonatomic, strong) UILabel *usernameLabel;
#property (nonatomic, strong) UILabel *emailLabel;
// and more labels
When you want to add these to your view controller's view, you could do something like:
- (void)addSubviewsAtPoint:(CGPoint)location
{
self.containerView = [[UIView alloc] initWithFrame:[self frameForContainer:location]];
[self.view addSubview:self.containerView];
self.usernameLabel = [[UILabel alloc] initWithFrame:[self frameForUsernameLabel]];
[self.containerView addSubview:self.usernameLabel];
self.usernameLabel.text = self.username;
self.emailLabel = [[UILabel alloc] initWithFrame:[self frameForEmailLabel]];
[self.containerView addSubview:self.emailLabel];
self.emailLabel.text = self.email;
// etc.
}
But it also means that when you remove the subviews, you not only have to remove the container view from your view hierarchy, but you also have to remember to nil all of the properties for all of those subviews:
- (void)removeSubviews
{
[self.containerView removeFromSuperview];
self.containerView = nil;
self.emailLabel = nil;
self.usernameLabel = nil;
// etc.
}
This introduces a maintenance issue, that every time you add a new control via addSubviewsAtPoint, that you also have to remember to add it to removeSubviews, too, or else you might be hanging on to the control well after you've removed it from the screen.
To simplify your life, you might make all of these properties weak (with the intuition being that it's the view that owns these subviews, not the view controller):
#property (nonatomic, weak) UIView *containerView;
#property (nonatomic, weak) UILabel *usernameLabel;
#property (nonatomic, weak) UILabel *emailLabel;
// etc.
But now, using ARC, your addSubviewsAtPoint no longer works, because when you assign an object to a weak property, if there are no other strong references, it will be immediately become nil:
self.containerView = [[UIView alloc] initWithFrame:[self frameForContainer:location]];
[self.view addSubview:self.containerView]; // FAIL!!! self.containerView will be nil!
So, instead, we employ that local variable pattern of your question to ensure that the controls are not prematurely deallocated while we're adding them to our view:
- (void)addSubviewsAtPoint:(CGPoint)location
{
UIView *containerView = [[UIView alloc] initWithFrame:[self frameForContainer:location]];
[self.view addSubview:containerView];
self.containerView = containerView;
UILabel *usernameLabel = [[UILabel alloc] initWithFrame:[self frameForUsernameLabel]];
[containerView addSubview:usernameLabel];
usernameLabel.text = self.username;
self.usernameLabel = usernameLabel;
UILabel *emailLabel = [[UILabel alloc] initWithFrame:[self frameForEmailLabel]];
[containerView addSubview:emailLabel];
emailLabel.text = self.email;
self.emailLabel = emailLabel;
// etc.
}
And, as a result, because we're using weak properties, our removal of those subviews is now much simpler, as we don't have to nil all of those properties when we remove the containerView from our view controller's view:
- (void)removeSubviews
{
[self.containerView removeFromSuperview];
// because all of those `containerView` subviews were `weak`,
// we don't have to manually `nil` them
}

IBOutlet doesn't react to code

I created a UIImageView in IB and connected it to an IBOutlet:
#property (nonatomic, retain) IBOutlet UIImageView *alertImage;
However, the UIImageView doesn't react to my code at all. I tried setting the outlet and then changing a property to test:
alertImage = [[UIImageView alloc] init];
alertImage.hidden = YES;
But the image didn't disappear. What am I doing wrong?
The problem is that you are re-initializing alertImage, so it's not connected to the "original" one defined in IB anymore.
Simply just use:
alertImage.hidden = YES;
Drop that line: alertImage = [[UIImageView alloc]init];
Write only alertImage.hidden = YES;
You declared your IBOutlet as a property, so why don't you use it as a property?
Try self.alertImage.hidden = YES;

UILabel is not updating it's text, IOS

I am stuck at a simple task, I have uilabel on my uiviewcontroller's interface file. I want to update that label via some methods. It doesn't update the label.
.h
UIViewController
{
UILabel *pictureNameLabel;
}
#property (nonatomic, strong) IBOutlet UILabel *pictureNameLabel;
.m
#synthesize pictureNameLabel=_pictureNameLabel;
- (void)viewDidLoad
{
_pictureNameLabel = [[UILabel alloc] init];
_pictureNameLabel.textColor=[UIColor blackColor];
_pictureNameLabel.text=#"Try";
}
How can I fix that issue?
You don't need to alloc the label. It's already alive and get's awaken from the nib.
.h
UIViewController
{
//UILabel *pictureNameLabel;
}
#property (nonatomic, strong) IBOutlet UILabel *pictureNameLabel;
.m
#synthesize pictureNameLabel=_pictureNameLabel;
- (void)viewDidLoad
{
//_pictureNameLabel = [[UILabel alloc] init];
_pictureNameLabel.textColor=[UIColor blackColor];
_pictureNameLabel.text=#"Try";
}
Your direct problem is the line:
_pictureNameLabel = [[UILabel alloc] init];
In -ViewDidLoad. It's creating a new variable, and having the pictureNameLabel property point to it, causing you to lose your reference to the one created in Interface Builder. Remove that line, and everything should work fine.
If you've created an element via Interface Builder, you do not need to alloc & init it yourself, along with adding it to the view in the appropriate spot, as it's automatically done for you, and the property is already set to point to it. If you're manually creating a new view, you do need to do that... but you'd also need to add it somewhere in the view hierarchy as a subview.
Not related to your problem, but you have also created a variable named UILabel *pictureNameLabel;. I'd assume you created this variable to be the backing variable for the synthesized property pictureNameLabel... but, you synthesized that to _pictureNameLabel, which means you now have two variables, _pictureNameLabel and pictureNameLabel. This is almost certainly a mistake. You should either remove the manual definition of pictureNameLabel, or rename it to something distinct if you actually intend to use it separately from the property with the same name. Having it is likely to just lead to confusion & bugs down the road.
your label has already exists on your xib file, and you can set the textcolor, text on interface bulider.

iphone dealloc property

I run the analyse build on Xcode, and get a warning for a leak because of an object that is a property and instance var
.h
UIView *_transparentView; }
#property (nonatomic, retain) UIView *transparentView;
.m
#synthesize transparentView = _transparentView;
self.transparentView = [[UIView alloc] initWithFrame:transparentViewFrame];
- (void)dealloc {
[_transparentView release];
so I release the ivar on dealloc, but how to release the property?, [self.transparentview release] ??
As Tom has answered replace the line that assigns the "transparentView" with:
self.transparentView = [[[UIView alloc] initWithFrame:transparentViewFrame] autorelease];
when you any value to a retained property you should you should release the assigned value if you are done with it, and release the property when deallocating the class.

Resources