This is an offshoot from a previous question, is this bad practice (using the property to set iVars)?
// Designated initializer 001
- (id)initWithName:(NSString *)newName andType:(NSString *)newType {
self = [super init];
if(self) {
[self setName:newName];
[self setType:newType];
}
return self;
}
or should I be using ...
// Designated initializer 002
- (id)initWithName:(NSString *)newName andType:(NSString *)newType {
self = [super init];
if(self) {
name = [newName retain];
type = [newType retain];
}
return self;
}
I have been using version 001, but have been led to believe that using properties to access iVars in either init or dealloc is bad practice.
EDIT: Added retain to version 002
Gary.
Yes, Apple discourages using accessors in init or dealloc, because they can have side effects beyond merely setting an instance variable. These are obviously undesirable in an uninitialized or destroyed object.
Exact quote from the docs: "The only places you shouldn’t use accessor methods to set an instance variable are in init methods and dealloc."
Related
In my class dealloc method I have
- (void) dealloc
{
[searchField release];
[super dealloc];
}
Where searchField is defined in the class variables.
#interface SearchCell : UITableViewCell
{
UISearchBar *searchField;
id delegate;
}
The class is used with the following way:
if (indexPath.section == 0)
{
SearchCell *mycell = [[SearchCell alloc] init];
[cell setDelegate:self];
return [mycell autorelease];
}
searchField is created here:
- (id) init
{
self = [super initWithFrame:CGRectZero];
[self create];
return self;
}
- (void) create
{
searchField = [[UISearchBar alloc] initWithFrame:CGRectZero];
searchField.autocorrectionType = UITextAutocorrectionTypeNo;
[self addSubview:searchField];
}
Do I need to use [searchField release]; in my dealloc? The application crashes with message: "*[UISearchBar respondsToSelector:]: message sent to deallocated instance *".
No, don't release it there. Since you return it with "autorelease", the next run through your event loop will automatically decrease the retain count.
When you finally hit your "dealloc" method, the retain count is already 0 and your app will raise an exception.
You need to be aware of object ownership rules. In manual retain & release environment (as opposed to ARC) the rules for Objective-C objects can be remembered with the acronym NARC.
If you have a reference to an object that you created fresh using new or alloc, or any object that you called retain on, or any object you created by calling copy or mutableCopy on another, or any object created by any methods that start with any of these names (e.g. newInstance), then
you own that object and before you are deallocated you must release it.
But, if you didn't do any of those things, then you do not own that object, and must not release it. source
In your case - you don't show the code where you set the value searchField, but from the crash I'd imagine you never take ownership of the object. Show the code where you assign to searchField and we'll all know for sure.
EDIT: You do take ownership by creating it with alloc. You need to keep the release there in dealloc. Is it being released from somewhere else? Or is the cell itself possibly getting overreleased?
Why do we always do this when creating constructors in Objective C?
self = [super init];
if ( self ) {
//Initialization code here
}
you can create constructor and destructor in objective-c with
-(id) init
{
self = [super init];
if(self)
{
//do something
}
return self;
}
-(void) dealloc
{
[super dealloc];
}
We reassign to self because [super init] is allowed to return a different object than the one it was called on. We if (self) because [super init] is allowed to return nil.
self is a class based on some superclass (e.g. UIViewController, NSObject - see your interface file to be sure which one). The superclass might need some form of initialization in order for the subclass to work as expected. So by first initializing the superclass we make sure default properties and the like are set. Without initializing the superclass first, we might experience some very unexpected behavior, especially in more complex objects like ViewControllers and the like.
Read this apple document on initialization
http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/ObjectiveC/Chapters/ocAllocInit.html
On the CS193p course they says that in an init method there should be an if statement to check if the [super init] works:
if (self = [super init]) {
self.someProperty = parameter;
}
return self;
I don't understand why this is done, as if the [super init] returns nil, the method itself will also return nil, no matter the outcome of the if statement?
EDIT: The question is; why put self = [super init] inside an if statement. (Not: Why have self = [super init] at all)
That's make sense because in some cases [super init] can return nil, and in this case if you try to access some ivar you'll get crash.
Example:
-(id) init {
self = [super init];
some_ivar = [[NSObject alloc] init]; //compiler treats this as self->some_ivar
//if self == nil you'll get EXC_BAD_ACCESS
return self;
}
Apple explains this particular concept in detail in there Objective-c programming guide. (Scroll down to the section "Handling Initialization Failure")
However Apple example code often keeps the self assignment outside the if statement
self = [super init];
if (self) {
//init stuff...
}
return self;
And if you are using the LLVM 2.0 compiler, it will give you a warning and tell you to wrap your statement in parentheses, too. Like this:
if ((self = [super init])) {
//init stuff...
}
return self;
It should be if(self = [super init]){.... init is an instance method and thus can only be called on an object that has already been alloc'd.
You need to call [super init] so that the superclass can do any one-time initialization that it needs to do. The init method returns a pointer representing the object that was initialized.
Assigning the result of [super init] back to self is a standard Objective-C convention. It's done in case the superclass, as part of its initialization work, returns a different object than the one originally created. NSString's, for example, do this
So it's self = [super init]
I have never seen [[super alloc] init], i've always used [super init], and as far as my knowledge, that's the convention
It doesn't have to be in an if statement.
you can very well have it like
self = [super init];
if ( self ) {
self.someProperty = parameter;
}
return self;
the if statement is there to check that self was initialized properly and it is safe to do self.someProperty = parameter
self = [[super alloc] init]; creates a base class
and the runtime will choke when you try to invoke subclass-only methods
later on.
self = [super init]; is a common sight in Objective-C
when the base class needs to initialize variable values or instantiate composite members,
otherwise they are created with Nil or (0) values.
In the case, if we return nil in the init method, what's happening with retain count and who is going to release this object?
As I undertand as soon as we called alloc (which will happen before init), the retain count will become 1. Now, init is called and let say for some reason it can't initialize the object, so it returns nil.
And it looks like now we have the object with retain count equal 1 and nobody has reference to it to call release.
Should we call [self autorelease] in init for such case or do something else?
See Allocating and Initializing Objects in the documentation.
Specifically, if you have an error in your initializer, then you release self and return nil:
- init
{
self = [super init];
if (self) {
if (... failed to initialize something in self ...) {
[self release];
return nil;
}
}
return self;
}
Now, consider what happens when you call [super init] and it returns nil. The above pattern has already been employed, what was self has been released, and the nil return indicates that the instance is gone. There is no leak and everyone is happy.
This is also a valid pattern (assume that self is an instance of MyClass):
- init
{
self = [super init];
if (self) {
... normal initialization here ...
} else {
self = [MyClass genericInstanceWhenInitializationGoesBad];
self = [self retain];
}
return self;
}
Since init is expected to return something that is retained (by implication of being chained from +alloc), then that [self retain], though it looks goofy, is actually correct. The self = [self retain] is just being extra defensive in case MyClass overrides retain to do something weird.
Usually, you will call
self = [super init];
if (self == nil) {
return nil;
}
// do some init job here
return self;
It is not your job to autorelease the self, because when you have it, it is already nil,so even if you call [self autorelease];, it does nothing.
I think the NSObject init method has to deal with the object already
I just familiarise myself with the CLLocationManager and found several sample class definitions that contain the following init method:
- (id) init {
self = [super init];
if (self != nil) {
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
self.locationManager.delegate = self;
}
return self;
}
- (void)dealloc {
[self.locationManager release];
[super dealloc];
}
I don't understand why the iVar would be autoreleased. Does this not mean it is deallocated at the end of the init method?
I am also puzzled to see the same sample codes have the iVar release in the dealloc method.
Any thoughts?
'
The locationManager is a property that is likely set with the retain attribute.
Basically, if you only write:
self.locationManager = [[CLLocationManager alloc] init];
the left-side self.locationManager setter retains a reference to the allocated CLLocationManager. But the right-side CLLocationManager reference is itself never released. The retain count for this manager never hits zero and the object never goes away — this causes a memory leak.
There are two ways to address this. Either autorelease the allocated object as you've seen in the code snippet you cited — or you assign the allocated object to a temporary variable, retain the temporary variable to the locationManager property, and then explicitly release the temporary variable:
CLLocationManager *_temporaryReference = [[CLLocationManager alloc] init];
self.locationManager = _temporaryReference; // this is retained
[_temporaryReference release];
Both approaches are equivalent, in terms of memory management. Some prefer this second approach because they don't like waiting for the autorelease pool to be "emptied", especially on a low-memory device like an iPhone, and this provides tighter control over an object's lifespan.
Apple's Objective-C Programming Language documentation explains this attribute in more detail.
There is an alternative without the temporary variable or the autorelease:
locationManager = [[CLLocationManager alloc] init];
Without using the self.locationManager you are not calling the setter method of the class for that variable and as a result not increasing the retain count to 2. The compiler changes these assignments into [self setLocationManager: locationManager];. This assumes that you have prototyped the variable as retain.
If it is a class variable (which it is) you can just make the assignment. It is debatable whether this is good coding practice but in my opinion it depends on where it is in the class initiation.
If your self.locationManager is a property that retains it, then it sets the retain. By doing alloc you do set the retain count to +1, which means that by the end of the function it's +2. When you say autorelease, it'll be +1 (because of the retaining property). You could also explicitly release it after setting it to the property, but what you're doing is less code and easy to read.