If you have an object with a property which has a retain setter, which of these is best practice?
1
-(id)init {
if((self = [super init])) {
self->_retainingProperty = [[NSObject alloc] init];
}
return self;
}
2
-(id)init {
if((self = [super init])) {
self.retainingProperty = [[NSObject alloc] init];
[self.retainingProperty release];
}
return self;
}
3
-(id)init {
if((self = [super init])) {
NSObject *obj = [[NSObject alloc] init];
self.retainingProperty = obj;
[obj release];
}
return self;
}
All of these would be coupled with a release in dealloc
Perhaps there is another way I've missed.
I generally just do:
- (id ) init
{
self = [super init];
if ( self )
{
retainingProperty = [[NSObject alloc] init];
}
return self;
}
I would not suggest #2 or #3, unless you are aware they might invoke KVO stuff that you do not intend.
All of the above are fine and broadly equivalent. You can also access the member variable using just its name:
-(id)init {
if((self = [super init])) {
_retainingProperty = [[NSObject alloc] init];
}
return self;
}
Property setters are really just convenience methods to ensure the retain/release dance is done correctly, so if you are doing things correctly you can do without them.
If you've added some custom logic to the setter method, you may want to ensure it's called by always using the property setter syntax. Or alternatively, you may want to deliberately sidestep that logic in some instances, and therefore avoid using it some of the time. It's up to you - whatever works for your use case.
At least two other ways immediately spring to mind. There's direct ivar access:
- (id) init
{
if ( self = [super init] )
{
_retainingProperty = [[NSObject alloc] init];
}
return self;
}
Or you could use the autorelease pool:
- (id) init
{
if ( self = [super init] )
{
self._retainingProperty = [[[NSObject alloc] init] autorelease];
}
return self;
}
Depending on the class of the property, there may also be convenience methods equivalent to the latter.
Personally, I would pretty much always go with the direct ivar access. It is usually considered bad form to call property accessors in init, because they may have side effects that you wouldn't want to happen while the object is incompletely initialised.
Similar considerations apply in dealloc, btw: it's better to release your ivars directly rather than using the property accessors.
Related
Assuming our -init method only invokes messages on self, why is it common to check if self != nil if messaging nil has no effect?
Let's say we have an initializer as follows:
- (id)init
{
self = [super init];
if (self) {
[self doThis];
[self setFoo:#"Bar"];
}
return self;
}
Instead of checking self, we could write:
- (id)init
{
self = [super init];
[self doThis];
[self setFoo:#"Bar"];
return self;
}
Now if for some reason [super init] returns nil, there would be no difference in the outcome of the method as far as I know. Why then do we constantly perform this check?
You can send a message to nil, but you cannot access the instance variables of nil. You'll get an EXC_BAD_ACCESS exception.
Consider a class that has instance variables:
#implementation MyObject {
int instanceVariable;
}
- (id)init {
self = [super init];
instanceVariable = 7;
return self;
}
What happens if [super init] returns nil in this example? You will try to access that instanceVariable off of a null pointer and you will get an exception.
Even if you're not accessing any instance variables, other things can certainly go wrong if you don't check for self == nil. You can easily leak malloc-allocated memory or file handles, or pass yourself to some method that's not expecting nil.
Other answers claim that you can leak objects if you don't check for nil. For example:
#implementation MyObject
#synthesize someProperty; // assume it's an NSObject *
- (id)init {
self = [super init];
[self setSomeProperty:[[NSObject alloc] init]];
return self;
}
This won't leak under ARC, even if self is nil. Under manual reference counting (MRC), this example will leak whether self is nil or not, because there's nothing to balance the +1 retain count from [NSObject alloc].
The proper way to do it under MRC is this:
- (id)init {
self = [super init];
[self setSomeProperty:[[[NSObject alloc] init] autorelease]];
}
or this:
- (id)init {
self = [super init];
NSObject *object = [[NSObject alloc] init];
[self setSomeProperty:object];
[object release];
return self;
}
Neither of those will leak, whether self is nil or not.
If you bypass the setter method, like this, you'll just crash if self is nil:
- (id)init {
self = [super init];
_someProperty = [[NSObject alloc] init];
return self;
}
If [super init] did in turn return nil, then you would end up possibly allocating more objects that will never be used, since when you return self, you will return nil; So those objects will not be released.
I'm having problems with a leak in the init method of a class I have created. To keep it simple, I have the following (simplified) problem:
ViewController initialises an instance of
ClipData class which initialises an instance of
AnimationData class which initialise a string
ViewController:
myClipData = [[ClipData alloc] init];
ClipData:
- (id)init
{
self = [super init];
if (self) {
animData = [[AnimationData alloc] init]; //LEAK HERE
}
return self;
}
AnimationData:
- (id)init
{
self = [super init];
if (self) {
name = [NSString string];
}
return self;
}
All the objects in the classes are declared as (nonatomic, retain). I'm aware that doing this bumps up the retain count, but how do I initialise the AnimationData without leaking the animData???
Probably a very stupid question, so any help much appreciated.
Thanks,
Duncs
You are never releasing the animData. You need to add dealloc to your class:
- (void)dealloc {
[animData release];
[super dealloc];
}
Similarly, you need to add a similar dealloc to AnimationData.
On a related note, you need to retain and later release the string created in -[AnimationData init], what you are doing right now is essentially a noop, except that it leaves behind a garbled pointer.
When you have an alloc you must also have a release.
You should also reference the properties through self so you access the properties rather than the underlying members.
So you should really do :
ClipData *clip = [[ClipData alloc] init];
self.myClipData = clip;
[clip release];
And
if (self) {
AnimationData *data = [[AnimationData alloc] init];
self.animData = data;
[data release];
}
Make sure you also release the properties in the dealloc of the class by setting them to nil.
self.myClipData = nil;
self.animData = nil;
Without automatic reference counting you often write code like this, when adding a new class:
assuming the classname is "Foo"
+ (id) foo
{
return [[[self alloc] init] autorelease];
}
- (id) init
{
self = [super init];
// do some initialization here
return self;
}
Well, how are you supposed, to write this for arc?
Just like the code below?
+ (id) foo
{
return [[self alloc] init];
}
- (id) init
{
self = [super init];
// do some initialization here
return self;
}
Yes. Are you expecting something different?
I have a class with the following init method:
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
StateStack* s = [[StateStack alloc] init];
state = s;
[s push:NONE]; //<--EXC_BAD_ACCESS on load here
[s release];
}
return self;
}
And StateStack has the following init code:
- (id)init {
self = [super init];
if (self) {
NSMutableArray* s = [[NSMutableArray alloc] init];
stack = s;
[s release];
NSLog(#"%d",[stack retainCount]);
}
return self;
}
Oddly, if I remove the NSLog line, the EXC_BAD_ACCESS moves to StateStack's dealloc method:
- (void)dealloc {
[stack release]; //<--EXC_BAD_ACCESS
[super dealloc];
}
Searching around seems to suggest that EXC_BAD_ACCESS is caused by overreleasing, but I can't see how I've overreleased anything. Does anyone know what the cause might be?
In your init function:
StateStack* s = [[StateStack alloc] init];
state = s;
[s push:NONE]; //<--EXC_BAD_ACCESS on load here
[s release];
you are allocating an instance of StateStack; this gets a retain count of 1. Then at the end of the function you call release, retain count goes to 0 and the object is ready to be released. So, when later dealloc is executed, the state ivar is sent another release and that is causing the bad access. You don't need to release s, since you want that state be retained. The same error pattern occurs in the other init method.
This would be correct:
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
StateStack* s = [[StateStack alloc] init];
state = s;
[s push:NONE]; //<--EXC_BAD_ACCESS on load here
}
return self;
}
- (id)init {
self = [super init];
if (self) {
NSMutableArray* s = [[NSMutableArray alloc] init];
stack = s;
}
return self;
}
NB: I don't want to generate misunderstandings. Using retain count to check for correct memory allocation is useless. This is true. Anyway, reasoning in terms of retain count helps understanding what happens when you allocate/release/autorelease an object. It is the basic mechanism, but it is too difficult to track it usage to check for correctness of memory management.
state = s is not copying the NSMutableArray object, it's just copying the pointer to it. So when you call [s release] the object referred to by both s and state is deallocated. You'll get an EXC_BAD_ACCESS whenever you use either from that point on.
Also, don't use [object retainCount] to debug memory management problems. It lies. Google NSZombies.
- (id)init{
self = [super init];
if (self) {
// Initialization code here.
state = [[StateStack alloc] init];
[state push:NONE];
}
return self;
}
StateStack
- (id)init {
self = [super init];
if (self) {
stack = [[NSMutableArray alloc] init];
}
return self;
}
I am new to Objective-C and I am confused about this retain-release thing. Are parameters retained automatically? Do I need to release them?
Here is my code. Did I do the retain-release thing (and everything else) correctly?
#import "ACStringTokenizer.h"
#implementation ACStringTokenizer
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
- (id)initWithStr:(NSString *)theString
{
self = [super init];
if (self) {
string = [theString retain];
delimiters = #" ";
doesReturnDelims = NO;
}
return self;
}
- (id)initWithStr:(NSString *)theString andDelims:(NSString *)theDelimiters
{
self = [super init];
if (self) {
string = [theString retain];
delimiters = [theDelimiters retain];
doesReturnDelims = NO;
}
return self;
}
- (id)initWithStr:(NSString *)theString andDelims:(NSString *)theDelimiters andDoesReturnDelims:(BOOL)returnDelims
{
self = [super init];
if (self) {
string = [theString retain];
delimiters = [theDelimiters retain];
doesReturnDelims = returnDelims;
}
return self;
}
- (int)countTokens
{
return numberOfTokens;
}
- (BOOL)hasMoreTokens
{
return ![queue isEmpty];
}
- (NSString *)nextToken
{
return [queue remove];
}
- (void)dealloc
{
[string release];
[delimiters release];
[queue release];
[super dealloc];
}
#end
Thanks in advance.
P.S. How do I make init with no parameters invalid?
This might be better suited to http://codereview.stackexchange.com?
Anyway, a few points:
You should read up on the concept of Designated Initializer. In your case you'd probably make initWithStr:andDelims:andDoesReturnDelims: the designated initializer. Only this initializer may call [super init]. All other initializers call [self initWithStr:andDelims:andDoesReturnDelims:] instead of [super init].
There are some more elaborate ways to make init invalid, but if you want to disable it I'd simply make it return nil. However, I don't really see a reason why you'd want to do this in this particular case.
The retains in your init methods and the dealloc method seem to be alright. Method parameters are valid until the end of the method, if you want to keep them beyond that, e.g. in instance variables, you need to retain them (which you seem to have done correctly).
However, there's a whole lot of code omitted in your example so obviously I'm only commenting on what you've posted.
A parameter variable is not retained automatically, you only get the object by reference. To keep them you have to retain them, as you did. For me it looks perfectly fine what you did there.