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?
Related
i am new in ObjC and iPhone.
I downloaded an example of data sharing between multiple views. The basic approach is to create an data model object in the base UIApplication and get/set data from/to it. So in the init method i saw the following code:
- (id) init;
{
self.theAppDataObject = [[ExampleAppDataObject alloc] init];
[theAppDataObject release];
return [super init];
}
And after that, using delegate we can access this object.
id theDelegate = (id) [UIApplication sharedApplication].delegate;
ExampleAppDataObject* theDataObject;
theDataObject = (ExampleAppDataObject*) theDelegate.theAppDataObject;
So, my question is in the first code example. Why do we need to alloc memory for the theAppDataObject object, and immediately after that - release the object? Why don't we get nil when accessing this object later?
10x
I assume theAppDataObject is declared as #property (retain). Therefore, when setting the object by doing self.theAppDataObject (or [self setTheAppDataObject:]), this property will retain the ExampleAppDataObject. Therefore, you can release it afterwards.
When you alloc and init the ExampleAppDataObject, it's retain count goes up to 1. When you set the AppDataObject to this ExampleAppDataObject, it sends retain to it, so the retain count goes up to 2. You can then release your own ownership of the object; it won't get deallocated because theAppDataObject still has ownership.
If that makes sense.
That depends on how theAppDataObject property is defined.
If it provides a retaining setter-accessor the retain count of the appDataObject will flip to 2, one more than needed here.
So release one of it.
Better and more understandable way would be to write
if ( (self = [super init]) ) {
ExampleAppDataObject *myAppDataObject = [[ExampleAppDataObject alloc] init];
self.theAppDataObject = myAppDataObject;
[myAppDataObject release];
}
return self;
Iphone uses reference count based memory management model..See this tutorial first and then apple's technical documentation... theAppDataObject is a property (see the use of self. theAppDataObject) which should be retained for the above code to work..Any object which is retained should have a bonus plus 1 retain count...An object only gets released when its retain count gets to zero..
first things first: that code sample is terrible.
- (id) init
{
// assign self. super may return another address
self = [super init];
// then check for nil
if (self != nil) {
// then assign the ivar directly because you should
// not invoke methods of partially constructed objects
theAppDataObject = [[ExampleAppDataObject alloc] init];
}
// then return the address super returned
return self;
}
now for your questions:
Why do we need to alloc memory for the theAppDataObject object, and immediately after that - release the object?
self.theAppDataObject calls through the setter, which retains, copies, or assigns theAppDataObject. in this case, we could assume it is highly likely to be retained.
Why don't we get nil when accessing this object later?
release does not set a pointer to nil. it sends a message to the object which then decrements the retain count (in the typical case). what you might have expected in this case is an object which has been deallocated. that does not happen when the argument is retained because the reference count does not reach zero in the program you have shown. the object is still alive because it's been retained and the address of the object stored when the setter (self.theAppDataObject = arg) is called.
I have a view controller named SettingsViewController, and a custom UIView subclass named ViewAccounts. In SettingsViewController, I am using the code below to add ViewAccounts view into SettingsViewController.
if(!self.viewAccounts)
{
ViewAccounts *objViewAccounts= [[ViewAccounts alloc] initWithFrame:CGRectMake(0, yViews, 320, 400) withViewController:self];
[self.view insertSubview:objViewAccounts atIndex:0];
self.viewAccounts = objViewAccounts;
[objViewAccounts release];
}
The Problem is that the dealloc method of ViewAccounts (subclass of UIView) is not getting called. If I comment these both lines of code, then dealloc method gets called.
[self.view insertSubview:objViewAccounts atIndex:0];
self.viewAccounts = objViewAccounts;
I guess the problem is with retain count, at it gets incremented by 1 when we use insertSubview or addSubiew, but how to get rid of it. Is this the right approach to use it?
Just as a note: making viewAccount to assign can cause a Zombie down the road (in the original code snippet) because you release it in the last line.
I would suggest the following approach:
Make viewAccounts retain again
Don't use autorelease because you are not returning it from that method
Pay attention to dealloc method of the method in the snippet
This is how I would write the snippet with viewAccounts retaining
if(!self.viewAccounts)
{
ViewAccounts *objViewAccounts= [[ViewAccounts alloc] initWithFrame:CGRectMake(0, yViews, 320, 400) withViewController:self];
[self.view addSubview:objViewAccounts];
self.viewAccounts = objViewAccounts;
[objViewAccounts release];
}
...
- (void) dealloc {
[super dealloc];
[viewAccounts release];
...
}
What happened in your original code was that the retaining variable viewAccounts was not released properly at the end of the this class life cycle and so dealloc was not called.
BTW when you only assign the variable then you have to manage the retain yourself if you re-assign that variable especially if you NIL it. Assume that you set the variable to NIL then the snippet could be executed again. But then you overwrite the variable and you are not able to release the first value. If you use retain the Object-C will do that for you. As a rule of thumb I would always use retain for properties until you have enough experience to handle the other cases.
You could set your objViewAccounts to autorelease, since it will be retained by self.view anyway.
ViewAccounts *objViewAccounts= [[[ViewAccounts alloc] initWithFrame:CGRectMake(0, yViews, 320, 400) withViewController:self] autorelease];
Also regarding the retain count, if the property self.viewAccounts is declared as retain, that would also increase the retain count by 1.
I'm new to cocoa / objective-c and i'm struggeling with the releases of my objects. I have the following code:
gastroCategoryList = [[NSMutableArray alloc] init];
for (NSDictionary *gastrocategory in gastrocategories) {
NSString *oid = [gastrocategory objectForKey:#"id"];
GastroCategory *gc = [[GastroCategory alloc] initWithId:[oid intValue] name:[gastrocategory objectForKey:#"name"]];
[gastroCategoryList addObject:gc];
}
The analyzer shows me that the "gastrocategory" defined in the for is a potential memory leak. But i'm not sure if i can release this at the end of the for loop?
Also at the following code:
- (NSArray *)eventsForStage:(int)stageId {
NSMutableArray *result = [[NSMutableArray alloc] init];
for (Event *e in eventList) {
if ([e stageId] == stageId) {
[result addObject:e];
}
}
return result;
}
The Analyzer tells me that my "result" is a potential leak. But where should I release this?
Is there also a simple rule to memorize when i should use assign, copy, retain etc. at the #property ?
Another problem:
- (IBAction)showHungryView:(id)sender {
GastroCategoriesView *gastroCategoriesView = [[GastroCategoriesView alloc] initWithNibName:#"GastroCategoriesView" bundle:nil];
[gastroCategoriesView setDataManager:dataManager];
UIView *currentView = [self view];
UIView *window = [currentView superview];
UIView *gastroView = [gastroCategoriesView view];
[window addSubview:gastroView];
CGRect pageFrame = currentView.frame;
CGFloat pageWidth = pageFrame.size.width;
gastroView.frame = CGRectOffset(pageFrame,pageWidth,0);
[UIView beginAnimations:nil context:NULL];
currentView.frame = CGRectOffset(pageFrame,-pageWidth,0);
gastroView.frame = pageFrame;
[UIView commitAnimations];
//[gastroCategoriesView release];
}
I don't get it, the "gastroCategoriesView" is a potential leak. I tried to release it at the end or with autorelease but neither works fine. Everytime I call the method my app is terminating. Thank you very much again!
In your loop, release each gc after adding it to the list since you won't need it in your loop scope anymore:
gastroCategoryList = [[NSMutableArray alloc] init];
for (NSDictionary *gastrocategory in gastrocategories) {
NSString *oid = [gastrocategory objectForKey:#"id"];
GastroCategory *gc = [[GastroCategory alloc] initWithId:[oid intValue] name:[gastrocategory objectForKey:#"name"]];
[gastroCategoryList addObject:gc];
[gc release];
}
In your method, declare result to be autoreleased to absolve ownership of it from your method:
NSMutableArray *result = [[[NSMutableArray alloc] init] autorelease];
// An alternative to the above, produces an empty autoreleased array
NSMutableArray *result = [NSMutableArray array];
EDIT: in your third issue, you can't release your view controller because its view is being used by the window. Setting it to autorelease also causes the same fate, only delayed.
You'll have to retain your GastroCategoriesView controller somewhere, e.g. in an instance variable of your app delegate.
BoltClock's answer is spot-on as to the first part of your question. I'll try to tackle the rest.
Assign is for simple, non-object types such as int, double, or struct. It generates a setter that does a plain old assignment, as in "foo = newFoo". Copy & retain will, as their names imply, either make a copy of the new value ("foo = [newFoo copy]") or retain it ("foo = [newFoo retain]"). In both cases, the setter will release the old value as appropriate.
So the question is, when to copy and when to retain. The answer is... it depends. How does your class use the new value? Will your class break if some other code modifies the incoming object? Say, for example, you have an NSString* property imaginatively named "theString." Other code can assign an NSMutableString instance to theString - that's legal, because it's an NSString subclass. But that other code might also keep its own reference to the mutable string object, and change its value - is your code prepared to deal with that possibility? If not, it should make its own copy, which the other code can't change.
On the other hand, if your own code makes no assumptions about whether theString might have been changed, and works just as well whether or not it was, then you'd save memory by retaining the incoming object instead of unnecessarily making a copy of it.
Basically, the rule, which is unfortunately not so simple sometimes, is to think carefully about whether your own code needs its own private copy, or can correctly deal with a shared object whose value might be changed by other code.
The reason you can release gc after it is added to the gastroCategoryList is that when an object is added to an array, the array retains that object. So, even though you release your gc, it will still be around; retained by the gastroCategoryList.
When you are returning a newly created object from a method, you need to call autorelease. This will cause the object to be released only after the runtime leaves the scope of the calling method, thereby giving the calling method a chance to do something with the returned value.
Note that if your method starts with the word copy or new, then you should not autorelease your object; you should leave it for the calling method to release.
As for copy vs retain vs assign... as a general rule, copy objects that have a mutable version, such as NSArray, NSSet, NSDictionary, and NSString. This will ensure that the object you have a pointer to is not mutable when you don't want it to be.
Otherwise, use retain whenever you want your class to be ensured that an object is still in memory. This will apply to almost every object except for objects that are considered parents of your object, in which case you would use assign. (See the section on retain cycles here).
Also note that you have to use assign for non-object types such as int.
Read through the Memory Management Programming Guide a bit; it's quite helpful.
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.
Hello Stackoverflow fellow family members!
I've got question regarding memory management in iPhone.
What I did understand was below method
-(void) dealloc
{
// something else to release whatever
// such as Object Created using keyword 'alloc'
// but also Object destroy here its retain value reaches iff 0
// if I do put here NSLog(#"%d", [obj retainCount]); and when it reaches
// not equal to 0 means failure with memory leak.
[super dealloc];
}
So am I understand right? or It is still alight even if retain count reachs > 0 here?
The reason I ask about this question because,
I checked with
NSLog(#"%d", obj.retainCount);
to check the retain count of the object and received value 3. So I tried to release here 3 times to make retainCount here equal to 0, but compiler gives me critical error.
Please, I'm new to the memory de-allocation and retain, release.
Object that I used was 'UIImageView' object and created another instance as,
UIImageView *imageView = //da da~ with UIImage
UIImageView *instance;
// at this point retain count was '1'
instance = imageView;
//[imageView retain];
// at this point retain count was '2'
[self.view addSubView: imageView];
// at this point retain count was '3'
[imageView release];// crashes
// at this point retain count was '2'
but if I do
// but if I add retain on the 'instance = imageView'
// such as
instance = imageView; // then
[imageView retain];
// works but still count is 2...
Thank You.
retainCount is not a reliable debugging-tool:
other objects might still hold references to obj
there are objects that you can't destroy (e.g. string constants)
objects are deallocated if released with a retain count of 1
What you should take care of instead:
balancing references you have with the right amount of release/autorelease
using the Analyzer
using Leaks
The normal process for overriding dealloc is to release any objects that have been previously retained (or alloc'ed) by this instance.
So if somewhere else in the object you have called an alloc or retain method your dealloc would look like:
-(void)someOtherMethod
{
UIImageView *imageView = //da da~ with UIImage
UIImageView *instance;
instance = imageView;
[instance retain];
}
-(void) dealloc
{
//release any retained objects here
[instance release]
[super dealloc];
}
Note that it doesn't matter if the release count hasn't dropped to zero after your particular release, that just means that some other bit of code has also retained the object memory (and that other bit of code will be responsible for releasing it).
Hope this helps.
That's not correct and you should rarely use retainCount. It does not have to be 0 at this point as other objects could have references to the objects you are releasing. What is important to do in dealloc is to release objects that you have ownership on. Which would be objects created with alloc or new etc.