Should I retain or assign the viewcontroller in this case? - iphone

Interface
#property (nonatomic, retain) PracticalSignsMainViewController *practicalVC;
Implementation
if (self.practicalVC == nil) {
PracticalSignsMainViewController *vc = [[PracticalSignsMainViewController alloc] init];
self.practicalVC = vc;
[vc release];
}
I only want to make one viewcontroller object in this case and change the data that it acts upon. However is it correct to retain or assign this viewcontroller (and why so)? It is used in a navigation-hierachy.
Thanks.

Retain. Typically you want object properties to be retained, and primitive properties to be assigned. The answer here is really good: Use of properties for primitive types

If you're planning to cache a view controller that's used in a navigation, you would need to retain it. The reason is that while the navigation controller retains it temporarily, once the user hits the back button the navigation controller will send the view controller a release message. If you haven't retained the view controller anywhere else at that point it will be deallocated.
If you're creating a new instance each time, that would be fine, but obviously that would destroy a cached instance if the property uses assign semantics.

Firstly, your code is right, but it can be more simple.
If you did the following synthesize,
#synthesize practicalVC; // synthesize release the existing reference if has
the following code is same as your code.
self.practicalVC = [[[PracticalSignsMainViewController alloc] init] autorelease];
As you mentioned, if your app does not need many access to the view controller, you need not to have view controller's instance separately.
============== Modified ====================
And I modified my answer after seeing answers of #6NSString #Abizern.
Regarding autorelease and coding style.
1. autorelease,
#6NSString said that "it uses autorelease which many avoid unless returning an newly alloced object."
But even if you use "autorelease" in return statement, the retain count of the returned object is not immediately decreased. It will be decreased at an appropriate time.
So If we want to decrease the retaincount explicitly and immediately, we should use paired "release".
-(NSObject*)testAuto {
return [[[NSObject alloc] init] autorelease];
}
...
self.anObj = [self testAuto];
// the retainCount of anObj is 2
..
-(void)testAuto {
self.anObj = [[[NSObject alloc] init] autorelease];
}
...
[self testAuto];
// the retainCount of anObj is also 2
..
-(void)testAuto {
self.anObj = [[[NSObject alloc] init] autorelease];
[self.anObj release]; // Yes, yes, you may say this code does not look good. :)
}
...
[self testAuto]
// the retainCount of anObj is 1
...
As you see, (I naturally tested above code again) "autorelease"s both in return statement and in alloc statement are almost same. And if you want to manage retain count harder, we should use "release".
I think "autorelease" in alloc statement is better than one in return statement because "autorelease" in return can be omitted. :)
2. Coding style,
As the end of "1. autorelease", I think, autorelease in alloc statement would be more safe to avoid missing release.

Related

Allocating a property

Working on someone else's code. Came across a piece of code while analyzing the project
self.groupPicker = [[UIPickerView alloc] initWithFrame:CGRectMake(0,260,320,216)];
self.groupPicker.delegate = self;
self.groupPicker.showsSelectionIndicator = YES;
[self.view addSubview:self.groupPicker];
Where groupPicker is a UIPicker property. When analyzing the project I encountered a potential leak warning in this case. I have also noticed that the groupPicker property is not being released in the dealloc method. Nor is _groupPicker released anywhere in the project. What should be done in this case?
Should I remove the UIPicker property and just declare a UIPicker variable instead.
Should I just release groupPicker like [_groupPicker release];
What would be the retain count of groupPicker as it is retained once in the .h file and again being allocated as shown in the above piece of code.
1) No, it is perfectly fine to have the property, the problem is that it is being over retained. When you alloc/init the retain count is 1, then you use the retained property which increases the retain count again. The retain count is now 2 and assuming you release the object in dealloc, you end up with a retain count of 1, i.e. a leaked object.
There are many ways you can handle the problem. I think the best way is to autorelease the object on initialization. Like so
self.groupPicker = [[[UIPickerView alloc] initWithFrame:CGRectMake(0,260,320,216)] autorelease];
2) Anything you retain should be released in dealloc, so in dealloc you should
- (void)dealloc {
[_groupPicker release];
[super dealloc];
}
Watch out! When you set a property like
self.property1 = x;
and the property1 is declared as retain, the previous object in the property1 is released and the new object (x) is retained. This is why doing this:
self.property1 = [[x alloc] init];
when property1 is declared as retain, will retain x twice. (one for init, one for setting the property)
The correct way is declaring the object, setting to the property and then releasing
object x = [[class alloc] init];
self.property1 = x;
[x release];
This way, you give the "responsability" of releasing the object x to the property holder.
While using ARC for iOS5+ applications should be preferred, if you don't want to do that just use autorelease after init method.
You should use ARC (Automatic Reference Counting)
to do so got to edit>refactor>convert to objective c ARC
Either assign the UIPickerView to _groupPicker (or whatever the instance variable is named), or use an autorelease on the value as you assign it.
(The problem is that assigning to a retained property causes a retain, and there's already a retain on the object from the alloc.)

property assign

If I have one property like this, what is the diference of assign the value of the property of the first mode and the second mode?
#interface Prueba : NSObject{
CustomeClass *_cclass;
}
#property(nonatomic, retain)CustomeClass *cclass;
#end
#implementation Prueba
#synthesize cclass = _cclass
- (void)config{
// 1 This
self.cclass = [[CustomeClass alloc] init];
// 2 This or
CustomeClass *cc = [[CustomeClass alloc] init];
self.cclass = cc;
[cc release];
}
#end
:/
Your first example gives you an object with a retain count of two (wrong), whereas your second example gives you an object with retain count of one (right). The second method is preferred in non-ARC projects. Alternatively, you could also do either set the ivar yourself (which I don't like because you're not using the setter):
_cclass = [[CustomeClass alloc] init];
or use the setter as your examples do, but do an autorelease (which I don't like because you shouldn't defer your releases unless you have to):
self.cclass = [[[CustomeClass alloc] init] autorelease];
In your non-ARC project, your original second example is best (using a pointer, using your property's setter, then releasing your pointer), because for KVO you want to get in the habit of using the setter:
CustomeClass *cc = [[CustomeClass alloc] init];
self.cclass = cc;
[cc release];
There is no difference in the result except that in the second method you create an additional pointer. In both versions self.cclass will hold your object just fine.
The problem is that when you only release the object in your second mode, in the first mode you'll have a memory leak. Since the retainCount of an object is +1 when you allocate it, you assign a +1 object through your setter. This means, that you actually bump up the retainCount again. Now if you don't release the object after assigning it to your property, once it gets released from there the retainCount will only be reduced by 1. Thus letting an object with a retainCount of +1 float around in the memory, lost forever.
But because you are already asking about a better version, I want to introduce lazy instantiation to you. What you can do, is that you overwrite the getter method of the property in question and check if it has been allocated yet. If not, you allocate it inside your getter method and then return it. It would look something like this:
- (CustomeClass*) cclass
{
if(!_cclass)
{
_cclass = [[CustomeClass alloc] init];
}
return _cclass;
}
With this method you assign a +1 retained object to an internal variable, thus bypassing the setter and not increasing the retainCount. Also it's memory friendly, because you object only gets instantiated when you really need it. Now when you set your property to nil or some new object, the old object will be properly deallocated.
EDIT:
In response to Robert Ryan's comment I want to add the following:
This does not break KVO, or interfere with the assigned qualifies for your properties. If your property is marked as assign or weak, then lazy instantiation doesn't really make sense. If it's marked as retain or strong this way of instantiating an object is perfectly fine, especially when it is a property which you would assign anyway inside a config method.
Regarding KVO: the value which is assigned inside the getter can be seen as the initial/default value, so KVO still works. It will trigger when you use the setter to assign something else to the property. You wouldn't want KVO to trigger because of a default value, would you?

Custom UIButton Memory Management in dealloc

I am hoping to clarify the processes going on here.
I have created a subclass of UIButton whose init method looks like this:
- (id)initWithTitle:(NSString *)title frame:(CGRect)btnFrame {
self = [UIButton buttonWithType:UIButtonTypeCustom];
[self setTitle:title forState:UIControlStateNormal];
self.frame = btnFrame;
return self;
}
In my view controller I am creating one of these buttons and adding it as a subview:
myButton = [[CustomButton alloc] initWithTitle:#"Title" frame:someFrame];
[self.view addSubview:myButton];
In the view controller's dealloc method I log the retain count of my button:
- (void)dealloc {
NSLog(#"RC: %d", [myButton retainCount]); //RC = 2
[super dealloc];
NSLog(#"RC: %d", [myButton retainCount]); //RC = 1
}
The way I understand it, myButton is not actually retained, even though I invoked it using alloc, because in my subclass I created an autorelease button (using buttonWithType:).
In dealloc, does this mean that, when dealloc is called the superview releases the button and its retain count goes down to 1? The button has not yet been autoreleased?
Or do I need to get that retain count down to zero after calling [super dealloc]?
Cheers.
This deserves two answers.... one for the specific question and one for how memory is managed when the instance is replaced in -init (this one).
Initializers are an odd bird in the Objective-C memory management world. In effect, you are managing self. On entry, self is retained. On exit, you are expected to return either a retained object -- doesn't have to be the same object as self -- or nil.
So, breaking the standard idiom of [[[Foo alloc] init] autorelease] down:
id x = [Foo alloc]; // allocates instance with RC +1
x = [x init]; // RC is preserved, but x may differ
[x autorelease]; // RC -1 (sometime in future)
Note that all retain counts [RC] are expressed as deltas.
Thus, in the init method, you typically don't change the retain count of self at all!
However, if you want to return some other object, you need to release self and retain whatever you are going to return (whether allocated then or previously allocated somewhere else, say when an object is retrieved from a cache).
Specifically, with everything blown out into individual expressions because this answer is being overly pedantic:
- init {
[self release];
self = nil;
id newObject = [SomeClass alloc];
newObject = [newObject init];
if (newObject) {
self = newObject;
... initialize self here, if that is your fancy ...
}
return self;
}
This is more than a little bit tricky. I have summarized my answer in 5 parts:
Creating a custom init method that returns a different object
WARNING: beware of illegal memory access!
How to properly transfer ownership of the button to its parent view
Specific answers to specific questions
A suggestion for improvement
Part 1 : Creating a custom init method that returns a different object:
This is an example of a very special case, namely that the object returned from -initWithTitle:frame: is not the same "object" that was sent the message in the first place.
Normally speaking, the process goes like this:
instance = [Class alloc];
[instance init];
...
[instance release];
Of course, the alloc and init calls are usually grouped together into one line of code. The key thing to notice here is that the "object" (nothing more than an allocated block of memory at this point) which receives the call to init has already been allocated. If you return a different object (as in your example), you are responsible for releasing that original block of memory.
The next step would be to return a new object that has the proper retain count. Since you are using a factory method (+buttonWithType:), the resulting object has been autoreleased, and you must retain it to set the proper retain count.
Edit: A proper -init method should explicitly test to make sure that it is working with a properly initialized object before it does anything else with that object. This test was missing from my answer, but present in bbum's answer.
Here is how your init method should look:
- (id)initWithTitle:(NSString *)title frame:(CGRect)btnFrame {
[self release]; // discard the original "self"
self = [UIButton buttonWithType:UIButtonTypeCustom];
if (self == nil) { return nil; }
[self retain]; // set the proper retain count
[self setTitle:title forState:UIControlStateNormal];
self.frame = btnFrame;
return self;
}
Part 2: WARNING: beware of illegal memory access!
If you are allocating an instance of CustomButton, and then replacing it with an instance of UIButton, you could easily cause some very subtle memory errors. Let's say CustomButton has some ivars:
#class CustomButton : UIButton
{
int someVar;
int someOtherVar;
}
...
#end;
Now, when you replace the allocated CustomButton with an instance of UIButton in your custom init... method, you are returning a block of memory that is too small to hold a CustomButton, but your code will continue to treat this block of code as if it is a full-sized CustomButton. Uh oh.
For example, the following code is now very, very bad:
- (id)initWithTitle:(NSString *)title frame:(CGRect)btnFrame {
[self release]; // discard the original "self"
self = [UIButton buttonWithType:UIButtonTypeCustom];
[self retain]; // set the proper retain count
someOtherVar = 10; // danger, Will Robinson!
return self;
}
Part 3: How to properly transfer ownership of the button to its parent view:
As for your view controller's dealloc method, you will have to call [myButton release] if you have initialized the button as shown. This is to follow the rule that you must release anything that you alloc, retain or copy. A better way to deal with this issue is to let the controller's view take ownership of that button (which it does automatically when you add the button as a subview):
myButton = [[CustomButton alloc] initWithTitle:#"Title"
frame:someFrame]; // RC = 1
[self.view addSubview:myButton]; // RC = 2
[myButton release]; // RC = 1
Now, you never have to worry about releasing that button again. The view owns it, and will release it when the view itself is deallocated.
Part 4: Specific answers to specific questions:
Q: The way I understand it, myButton is not actually retained, even though I invoked it using alloc, because in my subclass I created an autorelease button (using buttonWithType:).
Correct.
Q: In dealloc, does this mean that, when dealloc is called the superview releases the button and its retain count goes down to 1? The button has not yet been autoreleased?
Also correct.
Q: Or do I need to get that retain count down to zero after calling [super dealloc]?
Sort of :) The retain count may or may not drop down to zero at the point when you log it. Autoreleased objects may still have a retain count of one, since they effectively belong to the autorelease pool for the current run loop. For that matter, the view itself may still belong to a window which has not yet been released. The only thing you really need to worry about is balancing out your own memory management. See Apple's memory management guide for details. From the point of view of your viewController, you have allocated the button once, so you must release it exactly once. When it comes to your custom init... method, things get a little bit trickier, but the principle is the same. A block of memory has been allocated, so it must be released (part 1), and, (part 2) init should return an object with a retain count of one (to be properly released later on).
Part 5: A suggestion for improvement:
You could avoid most of the custom initializer mess by simply creating your own factory method in the same spirit as the one provided by UIButton:
+ (id)buttonWithTitle:(NSString *)title frame:(CGRect)btnFrame {
UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTitle:title forState:UIControlStateNormal];
button.frame = btnFrame;
return button;
}
Note that this approach can still result in memory access errors as identified in part 2
First:
Do not call retainCount
The absolute retain count of an object is next to useless. There are always better ways to reason about memory management in your application.
Next:
Your initWithTitle:frame: method is allocating and returning an instance of UIButton, not an instance of the subclass. If that is what you want, there is no need for a subclass at all.
If you really want an instance of a subclass of UIButton, that is going to be more difficult. A quick google search and a read of the documentation indicates that UIButton really isn't intended to be subclassed.
I just tried:
#interface FooButton:UIButton
#end
#implementation FooButton
#end
FooButton *f = [FooButton buttonWithType: UIButtonTypeDetailDisclosure];
NSLog(#"%#", f);
And it printed:
<UIButton: 0x9d03fa0; frame = (0 0; 29 31); opaque = NO; layer = <CALayer: 0x9d040a0>>
I.e. the sole method to be used to create UIButton instances quite explicitly does not allocate via [self class]. You could go down the path of trying to initialize the instance by hand ala UIView or UIControl, but that is likely a lot of trouble.
What are you trying to do?

(iphone) basic memory management question

I have used following 2 patterns to create a view.
#property (retain, nonatomic) SomeView* someView;
...
// First pattern
self.someView = [[SomeView alloc] initWithFrame frame];
// Second pattern
SomeView* aSomeView = [[SomeView alloc] initWithFrame];
self.someView = aSomeView;
[aSomeView release];
Now, looking back at this code, the first pattern's method should be changed to
self.someView = [[[SomeView alloc] initWithFrame frame] autorelease];
shouldn't it?
I feel dumb :(
Look at it like this:
[[SomeView alloc] initWithFrame: frame];
The above line creates an object and gives it a retain count of 1.
self.someView = [[SomeView alloc] initWithFrame: frame];
This line leaves it with a retain count of two, because the someView property is declared with retain:
#property (**retain**, nonatomic) SomeView* someView;
So, doing it this way leaves your someView property pointing to an object with retain count of 2. You can do it this way if you add an autorelease call to it:
self.someView = [[[SomeView alloc] initWithFrame: frame] autorelease];
Your second pattern is better, if you ask me. You create an object with a retain count of one. You assign it to a retaining property (now it has a retain count of 2) and then you release the original variable, leaving the object again with a retain count of 1. It's three lines where you might want only one, but it makes sense in the right context. Additionally, it's usually best to avoid using autorelease outside of an alloc or copy method since its usually an indication you don't fully understand memory management in Obj-C.
And as a commenter said in the comments to the question, don't feel dumb. None of this is intuitive at first. Nobody picks up a guitar and plays like Hendrix their first time.
Yes, you are right. autorelease means "release a bit later".
Yes, I think you should change that. With self.someView = you are calling the setter which increases the retain count.
Now, looking back at this code, 1's method should be changed to self.someView = [[[SomeView alloc] initWithFrame frame] autorelease];
shouldn't it?
correct
a)
SomeView * view = [[SomeView alloc] initWithFrame:frame];
self.someView = view;
[view release], view = nil;
b)
self.someView = [[[SomeView alloc] initWithFrame:frame] autorelease];
many people prefer b, simply because it is less to type.
i prefer an approach similar to a because:
defects (such as over-releasing) are often exposed near the call site, rather than when the pool is destroyed (this often means you have to load up Instruments in Zombie mode to locate the callsite)
it performs better and minimizes memory usage (in general, but not much in this specific case)
you have more opportunity to check for invalid states and results
you have a chance to init/configure the view/object for its usage before adding it to self, which is usually preferred

Unnecessary temporary variables when setting property values?

I'm following a book on iPhone development and there is a particular pattern I keep seeing in the example code that doesn't make much sense to me. Whenever a property is set they first assign a pointer to the new value for the property, then set the property to the pointer, then release the pointer. Example:
Interface:
#interface DoubleComponentPickerViewController : UIViewController {
NSArray *breadTypes;
}
#property(nonatomic, retain) NSArray *breadTypes;
#end
Class method:
- (void)viewDidLoad {
NSArray *breadArray = [[NSArray alloc] initWithObjects:#"White", #"Whole Wheat", #"Rye", #"Sourdough", #"Seven Grain", nil];
self.breadTypes = breadArray;
[breadArray release];
}
Is there any reason to do this instead of just doing the following?
- (void)viewDidLoad {
self.breadTypes = [[NSArray alloc] initWithObjects:#"White", #"Whole Wheat", #"Rye", #"Sourdough", #"Seven Grain", nil];
}
Thanks for the light that will no doubt be shed :)
Let me try and explain it in a different way.
A method that has alloc, copy or new in its name will allocate memory for an object, and gives ownership of that object to the caller, and it is the caller's responsibility to release that memory.
In your viewDidLoad method, you call a method that gives you ownership of an object. It is your method's responsibility to release it. However, before you do that, you want to do something with it - after all, that's why you alloc'ed it, not to just release it, but to do something useful with it.
Regardless of what it is that you want to do with it, you have to release it (or autorelease it*). In this case your use of the object is to pass it to self.breadTypes. self.breadTypes may not look like a method, but it is (it is a setter). You pass it breadArray. It does what it needs to with it. It might retain it for use later, or it might copy some info out of it, or make a copy of the entire thing. Your viewDidLoad doesn't really care. It assumes that self.breadTypes does what it needs to and when it returns, it doesn't care what you do with breadArray.
And what you do with it, is what you have to do with anything that you own - release (or autorelease* it).
That's why you have to use the temp variable, breadArray. You can't quite release the results from alloc on the same line, since the object would get released before self.breadTypes can have at it:
self.breadTypes = [[[NSArray alloc] initWithObjects:#"White", ..., nil] release];
Thus you are forced to assign to a temp variable, pass it to self.breadTypes, and then release the object that is saved in breadArray.
Now, you could try to do it this way so you don't use a temp variable:
- (void)viewDidLoad {
self.breadTypes = [[NSArray alloc] initWithObjects:#"White", #..., nil];
[self.breadTypes release];
}
but that is not very efficient since you are calling yet another method (self.breadTypes as a getter) that you didn't really need to if you have just stored the value in a temp variable.
*Now, as a responder said, you could use autorelease for an alternative version:
- (void)viewDidLoad {
self.breadTypes = [[[NSArray alloc] initWithObjects:#"White", ..., nil]
autorelease];
}
Apple urges us to think twice about whether we want to use autorelease vs. release. Autorelease may not be the best choice for every situation. I personally like to clean up after myself as soon as I possibly can, and not use autorelease needlessly. Autoreleased objects get released at the end of the execution of the run loop, for example soon after viewDidLoad returns. You should read up a bit more about autorelease (and memory management on the iPhone which is slightly different than MacOS X Cocoa), as I am oversimplifying it all.
BTW: If you retain an object, you are assuming ownership of it, and you will have the same responsibility again, to release it after you are done with it.
Yes. Those methods are alloc'ing the variables so they must be released. The fact that the property has a retain attribute means that when you say #synthesize breadTypes; the compiler is actually generating a setBreadTypes that properly releases the current breadType member and retains the new one. Thus your function must not retain the variable it alloc'ed.
You could, however write:
- (void)viewDidLoad {
self.breadTypes = [[[NSArray alloc] initWithObjects:#"White",
#"Whole Wheat", #"Rye", #"Sourdough",
#"Seven Grain", nil]
autorelease];
}
You'll want to brush up on Cocoa Memory Management