Given (arbitrarily):
CGRect frame = CGRectMake(0.0f, 0.0f, 100.0f, 30.0f);
What's the difference between the following two code snippets?
1.
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = frame;
2.
UIButton *button = [[[UIButton alloc] initWithFrame:frame] autorelease];
I think they're equivalent. Haha! Trick question you sneaky little punk!
Reasoning
-buttonWithType: returns an autoreleased UIButton object.
+[NSObject alloc] defaults scalar instance variables to 0, so buttonType should be 0, or UIButtonTypeCustom.
Pros & Cons
You could argue that it's clearer to use -buttonWithType: and set buttonType explicitly and that it's safer in case Apple changes UIButtonTypeCustom to be 1 instead of 0 (which will most certainly never happen).
On the other hand, you could also argue that it's clear & safe enough to use -initWithFrame. Plus, many of the Xcode sample projects, such as "TheElements" & "BubbleLevel," use this approach. One advantage is that you can explicitly release the UIButton before the run loop for your application's main thread has drained its autorelease pool. And, that's why I prefer option 2.
I would strongly suggest using the first approach (+buttonWithType), because that's the only way to specify the button type.
If you +alloc and -initWithFrame:, the buttonType is set to some standard value (not sure which, and this could change in later versions of the SDK) and you can't change the type afterwards because the buttonType property is read only.
The main (maybe the only) difference is in memory management: as you said, buttonWithType returns an autoreleased UIButton. This way you don't have to worry about releasing it. On the other hand, you don't own it, so you cannot release it when you want to (except, of course, if you drain the autorelease pool).
Calling explicitly [[UIButton alloc] initWithFrame:frame], instead, you dynamically alloc your button, so you own it and you're responsible for releasing it. If you plan to retain your button for some reason then maybe you should consider the second solution, but if, as in this case, you are autoreleasing it immediately, there's no big difference between the two way of creating a button...
Two options are the same but I prefer option 2 because it can handle memory management
You can use the first and do "[button retain];", so you will never lose the pointer.
I've went through the UIButton documentation, and here what i've found:
Discussion
This method is a convenience constructor for creating button objects with specific configurations. If you subclass UIButton, this method does not return an instance of your subclass. If you want to create an instance of a specific subclass, you must alloc/init the button directly.
I guess this is the trick. The alloc-initWithFrame is for subclasses.
Related
This is a basic memory management code. I am dealing with an old app that does not have ARC implemented.
My question: Is it ok to access an instance variable after adding it to a view & releasing it.
In my opinion it is probably not right to access an instance variable after calling release on it, but can anyone advice ?
demoView = [[DemoView alloc] initWithFrame:[self demoRect:newType]];
[self addSubview:demoView]
[demoView release];
Later in code access it:
[demoView setBackgroundColor:[UIColor whiteColor]];
demoView.title = #"something";
object is released later on like this:
[demoView removeFromSuperview];
demoView = nil;
When this line is called, the retainCount of demoView is 1.
demoView = [[DemoView alloc] initWithFrame:[self demoRect:newType]];
after this line
[self addSubview:demoView]
retainCount gets increased to 2.
If nothing else is done but the demoView is released later in dealloc, or just removed, it will still have a retainCount of 1.
That is why the developer calls the
[demoView release];
to keep the retainCount at 1.
P.S.
In the older retain/release paradigm, you would have to maintain the retainCount and keep that in check. retainCount gets increased on alloc, on getting added to a UIView/NSArray/NSDictionary and on calling retain
And retainCount decreases when you call release, remove from a UIView/NSArray/NSDictionary.
When you add a subview to a view, subview is retained by view.
So, if you use a subview after you release a subview, it will work. But it is a really bad practice to use some object you had already released.
It's generally best to use accessor methods rather than directly manipulating ivars in manually reference counted code. Consider rewriting the implementation along the following lines:
self.demoView = [[[DemoView alloc] initWithFrame:[self demoRect:newType]]
autorelease]];
[self addSubview:demoView];
This way, if you remove the demo view from its superview you can safely keep it in memory should you ever want to add it back to the view hierarchy later, or move it to a different superview. In any case, the demo view should get a release message in dealloc so it can get cleaned up at the end of its owner's lifetime.
I have read multiple times that we should not subclass a component (a UIButton for example) :
Why shouldn't I subclass a UIButton?
Subclassing a UIButton
The problem is when I use Interface Builder.
For example, I have a button with a precise appearance in a lot of my views. I can set them each time with IB (it's painful), or I can use a custom class to factorize the custom behavior and appearance.
It seems a bit contradictory to me that the only way to simplify the process with IB is to do it the way that everybody recommends against.
Is there a better solution ? Can I use a category with IB ?
Thanks.
You might be able to use the UIView appearance proxy. I don't know what all you're doing to your buttons but this might help:
Put this is your AppDelegate file in the application:didFinishLaunchingWithOptions: method
if([UIButton conformsToProtocol:#protocol(UIAppearanceContainer)]){
[[UIButton appearance] setBackgroundImage:[UIImage imageNamed:#"YourImage"] forState:UIControlStateNormal];
//modify any other UIButton properties you want to set globally
}
The second link you provided was pretty clear, and this is pretty much what apple itself states, subclass, but never mess with the internal structure.
Best example is iOS 7, now things are completely different and, for example, an application I'm maintaining had a subclassed UIControl, and now it has trouble running on the new iOS, simply because, it was built with assumptions on how the internal structure works (iterating the internal subviews replacing some things). You might not get your app rejected, but it will be a pain in the a** to maintain.
As a rule of thumb, anything you can do to an UIButton from the outside, something like this:
[myButton setBackgroundImage:... forState:...];
[myButton setTextColor:... forState:...];
myButton.titleLabel.font = ...
You can move it to the inside of a custom subclass method:
+ (UIButton*)fancyPantsButton
{
UIButton *button = [UIButton butonWithType:UIButtonTypeCustom];
[myButton setBackgroundImage:... forState:...];
[myButton setTextColor:... forState:...];
myButton.titleLabel.font = ...
return button;
}
You can also do this on init or awakeFromNib without problems (and I usually prefer the later).
UIAppearence is also an option, as was suggested by user hw731. Whatever floats your boat, really.
As for the second question, nib files pretty much create instance a class and then fill-in the things it stores using setValue:forKey: when loading (that's why you get an error like "class is not key-value compliant for something" when you screw up a nib), so if something is categorised when the nib is being loaded, then yes, nibs respect categories, as its simply using initWithCoder.. and then filling in the gaps.
And, by the same token, the nib file won't be able to fill-in custom properties, since it doesn't know about them, unless you explicitly add them on the "User Defined Runtime Attributes" in IB (iOS 5 onwards).
Another technique for nibs, is using
#property (strong) IBOutletCollection(UIButton) NSArray *buttons;
And then iterating and customising buttons accordingly (be it via a subclass, category, local method, ...). This method is really helpful if you want just a handful of custom buttons, but not enough to warrant using a subclass.
I don't see any reason that you shouldn't subclass UIButton, especially for your purpose of making configuration with IB easier. Neither of the links you provided explain why you shouldn't subclass, so their assertions don't seem reliable. On the other hand, the presence of UIButtonTypeCustom in UIButton.h gives the impression that the framework authors planned for UIButton subclasses.
I have a small question when programming objects in objective-C. I have an App that is just about complete and everything works fine. My question is that I set my objects to nil and release them at appropriate times.
But is this enough or when and where should I use removefromsuperview?
In the case of adding a UIButton to a UITableViewCell I add the UIButton with the following code:
UIButton *buttonReset = [UIButton buttonWithType:UIButtonTypeContactAdd];
buttonReset.frame = CGRectMake(250.0f, 7.0f, 75.0f, 30.0f);
[cell addSubview:buttonReset];
buttonReset addTarget:self action:#selector(resetSettings) forControlEvents:UIControlEventTouchUpInside];
buttonReset = nil;
[buttonReset release];
Do I also need to use
[buttonReset removeFromSuperview];
in this case?
buttonReset = nil;
[buttonReset release];
This doesn't make sense. You set a pointer to nil (null pointer) and then send a message to it. In most other languages this would result in a crash. In Objective-C it's allowed, but nothing will happen. You have to release before setting to nil. But you shouldn't do neither in this case, because buttonReset is an autoreleased object (you didn't use alloc/init to create it), so you don't own it and therefore you must not release it.
You also don't have to use removeFromSuperview in this case. You add a button (a subview) to your cell (the superview). The superview will hold a strong (retaining) reference of the button. When the cell is then released, it will also handle all of its subviews. You only have to remove it yourself when you actually want to do that, but not for memory management reasons.
If you didn't already know about it, you might want to consider using Automatic Reference Counting (ARC) in the future.
No, you should not call [buttonReset removeFromSuperview];, at least not right away: if you do, the button would disappear from screen (given the name of the method, this should come as no surprise). Moreover, you do not need to set your button to nil.
Calling removeFromSuperview is needed when you need the control to be dropped from the screen. If you also release it, the object representing your control would be destroyed. For example, if you added a button programmatically for a specific task, and have to remove that button once the task has been accomplished, calling removeFromSuperview is appropriate.
Calling removeFromSuperview on a view causes it to be removed from its superview. This will make the targetted view disappear from the screen with all the view it contains.
In your situation, I would just set the object to nil and be done with it.
See does removefromsuperview releases the objects of scrollview?.
There are interesting informations in it.
but it's worth digging deeper into this, because it's a very important
concept in ObjC. You should never call -release on an object you
didn't -retain explicitly or implicitly (by calling one of the Three
Magic Words). You don't call -release in order to deallocate an
object. You call it to release the hold you have put on the object.
Whether scrollview is retaining its subviews is not your business (it
does retain its subviews, but its still not your business). Whether
-removeFromSuperview calls -release is also not your business. That's betweeen the scrollview and its subviews. All that matters is that you
retain objects when you care about them and release them when you stop
caring about them, and let the rest of the system take care of
retaining and releasing what it cares about.
you should use just the
[buttonReset removeFromSuperview];
and then
buttonReset = nil;
as apple saying
If the receiver’s superview is not nil, the superview releases the receiver. If you plan to reuse a view, be sure to retain it before calling this method and release it again later as appropriate.
in UIView Referance
If I have some objects like UILabel, UIImageView, etc. on my XIB
But I don't using any IBOutlet to connect them
I reference these objects like follow
UILabel *label = (UILabel *)[[self.view subviews] objectAtIndex:x];
UIImageView *imageView = (UIImageView *)[[self.view subviews] objectAtIndex:x];
Do I need release this objects?
If you are not retaining this objects you don't has to release them. You only has to release the object that you create
No. If you don't call retain on it, then you don't have to release it.
However, your approach is very bad. Is any reason that stop your from creating IBOutlet connection? Even use viewWithTag: will do a better job. Because if you add a new view to nib, it is very likely to mess the order and you have to change every hardcoded index value.
You should release all the object what ever you created. it's good habit.
Otherwise you ll get memory problem.
You should not release it unless you alloc, init, retain, copy the object yourself.
Im struggling to solve in a very clean way a problematic involving memory overload (management).
Im having a serie of view that include other views, in my project I have a situation like this:
MainView
|_PageView
|_CustomButton
soo far soo good, easy as a cake. CustomButton have a delegate (protocol) in it for some reasons, so we have in PageView a "for cycle" that creates N CustomButtons, set the delegate as self in PageView (PageVew extend CustomButtonDelegate) and release the buttons afer attaching them like
{
CustomButton *customButton_ = [[CustomButton alloc] initWithFrame:CGRectMake(100.0,50+(i*55.0),200.0);
customButton.delegate = self;
[self addSubView:customButton_];
[customButton_ release];
}
soo far soo good again. Button will be press, PageView get the protocol method, do some code and voilĂ , done. One problem is that at one point, MainView must remove PageView, so In a method I call
[pageView_ removeFromSuperview];
[pageView release], pageView_ = nil;
pageView_ = [PageView alloc] initWithFrame.....];
and I recreate the object with other data to display.
I noticed that PageView never gets release and removed from the memory because its retainCount is exactly how many CustomButton I created inside PageView and assign the delegate to self plus one of course. My question is, what is the cleanest way to remove safely all the objects and be able to remove PageView too, free the memory (because Im loading a quite large amount of data to display in it) ?
Right now i'm doing:
Create in PageView a NSMutableArray, that I CustomButton the objects in
it, and before to remove PageView, I cycle it and set the delegate = nil and then release
each object, after I release the NSMutableArray (called "holder").
But the problem is that if I want to add more objects of different types with other protocols, adding to this array, can lead to other problems of retaining the objects.
Where do I lack guys, knowledge so I need to study more (quite sure I can say) or do I need to approach with another OOD?
Thank you guys, im going overload with this problem and my brain is stuck in a close road. :)
Looks like your CustomButton's delegate is a retain property of CustomButton. Delegate should be an assign property, not retain nor copy. See here.