5

I'm pretty new to Objective-C and I have a question.

I have created a custom class and tried to create overloads for Initialization:

- (id)init
{
    if (self = [super init]) {
        [self setIsCurrentCar:NO];
    }
    return self;
}

-(id) initWithID:(NSInteger)id {
    if(self = [self init]) {
        [self setID:id];
    }
    return self;
}

-(id) initWithID:(NSInteger)id CarYear:(NSString *)year {
    if(self = [self initWithID:id]) {
        [self setCarYear:year];
    }
    return self;
}

Let's say at one point, I call the -(id) initWithIDCarYear method.

I'd like to know the code above is structurally correct.

  • In this code, self is set for 3 times. Is there a better solution?
  • Do I have memory leak in this code? (using ARC)
  • Do I have to check for if(self = ...) always or it is a redundant code?

Thank you

@Edit Is the following code better?

-(id) initWithID:(NSInteger)id CarYear:(NSString *)year {
    if (self = [super init]) {
        [self setIsCurrentCar:NO];
        [self setID:id];
        [self setCarYear:year];
    }
    return self;
}
NarbehM
  • 345
  • 2
  • 13
  • The reason I'm afraid I'd have memory leak is I know that [super init], [self init] and [self initWithID:id] create 3 different instances, meaning that they occupy 3 different places in memory. With using ARC, when are they deallocated? – NarbehM Dec 21 '12 at 19:16
  • 3
    No, `init` doesn't create an instance. Only `alloc` does. –  Dec 21 '12 at 19:18

4 Answers4

9

While your code is ok, i would structure the init-calls in the reverse order, where the most detailed one is the designated initializer and the more general ones would bubble some default values up:

-(id) initWithID:(NSInteger)id 
         CarYear:(NSString *)year 
{
    if(self = [super init]) {
        _year = year;
        _id = id;
    }
    return self;
}

-(id)initWithID:(NSInteger)id 
{
    return [self initWithID:id CarYear:@"unset"];
}

-(id)init 
{
    return [self initWithID:0];
}

if calling one of the more general initializer would generate an illegal state, you could instead throw an error to prohibit using it.

let's assume, a car needs to have a ID, but not a year. It would be ok to use initWithID but using init would lead to an inconsistent state, so we want to force not to use it:

-(id)init 
{
    [NSException raise:NSInternalInconsistencyException 
                format:@"You must use -initWithID: or -initWithID:CarYear:", NSStringFromSelector(_cmd)];
    return nil;
}

  • In this code, self is set for 3 times. Is there a better solution?

see above

  • Do I have memory leak in this code? (using ARC)

No, everything is fine

  • Do I have to check for if(self = ...) always or it is a redundant code?

As I showed you: you can call different init methods in a chain. just the last in that chain needs to perform that.


-(id) initWithID:(NSInteger)id CarYear:(NSString *)year {
    if (self = [super init]) {
        [self setIsCurrentCar:NO];
        [self setID:id];
        [self setCarYear:year];
    }
    return self;
}

You should not use setters on self in init-methods, see Apple's docs.

John Topley
  • 113,588
  • 46
  • 195
  • 237
vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
  • Is this a common practice in Objective-C? I mean breaking down from bottom to top. I'm coming from C# and there, I'd probably break it from top to bottom meaning that the one overload with more details would call the one with less details. – NarbehM Dec 21 '12 at 20:04
  • In this way you avoid code duplication. C# might be different, as it distinguishes between methods and constructors. But in ObjC inits are still normal methods. And with methods I would alway call the more detailed methods with default parameters. – vikingosegundo Dec 21 '12 at 21:29
  • So, the properties should be set as `_IsCurrentCar = NO;` even if I do `@synthesize IsCurrentCar` instead of `@synthesize IsCurrentCar = _IsCurrentCar` ? – NarbehM Jan 19 '13 at 08:20
  • you dont need synthesize anymore. by using `_isCurrentCar = NO` you are bypassing the property and accessing the backing iVar (instance variable) directly. as explained in the apple docs, you should access iVars directly — no property (self.isCurrentCar = NO) or setter ([self setIsCurrentCar:NO]). – vikingosegundo Jan 19 '13 at 09:11
  • but if you would use `@synthesize isCurrentCar;` the backing iVar's name would be `isCurrentCar` – vikingosegundo Jan 19 '13 at 09:12
  • Thanks. This is very helpful. One last question, is that why `@synthesize IsCurrentCar` will create an iVar of `IsCurrentCar` instead of `_IsCurrentCar` by default? – NarbehM Jan 19 '13 at 10:07
  • 1
    historic reasons: for many years apple proposed not to use underscores, so many developer did not use it. but apple changed it's mind, underscore is preferred and synthesized, if no synthesize statement is found. if you would compile a source code from older times, when a synthesized statemnt would be needed and generate a backing variable with the same name without underscore with modern compilers, and if the default for auto synthesizing and explicit synthesizing would be the same (prefixing _), those code would break, as direct accessing the ivars would yield `identifier not found`. – vikingosegundo Jan 19 '13 at 10:26
  • 1
    BTW: you should familiarize yourself with Cocoa coding styles. variable names, objects name and method names should be camelCase with a small letter at the beginning. – vikingosegundo Jan 19 '13 at 10:28
3

I'd like to know the code above is structurally correct.

Yes. I don't see any problem with it.

In this code, self is set for 3 times. Is there a better solution?

That's pretty normal. I wouldn't bother changing that.

Do I have memory leak in this code? (using ARC)

No.

Do I have to check for if (self = ...) always or it is a redundant code?

You don't have to, but you definitely should. See this question for details.

Community
  • 1
  • 1
DrummerB
  • 39,814
  • 12
  • 105
  • 142
  • About if(self = ...), as I understand, each init, returns a new instance, therefore we have to set it to self all the time. Correct? – NarbehM Dec 21 '12 at 20:07
  • 2
    No. The class method `alloc` created (allocated) a new instance. Init just sets it up (sets default values of the instance variables etc.). You have to set the return value to self, because in some cases, init might return an other instance or `nil` in case the initialization was not successful. – DrummerB Dec 21 '12 at 20:14
2

It looks like you have one mandatory initialized variable and two that are effectively optional.

I'd recommend implementing an init method and two @property()s for the ID and carYear. That reduces the # of initializers and better reflects that usage contract of the class.

bbum
  • 162,346
  • 23
  • 271
  • 359
1

From my little knowledge... tried to create overloads for Initialization this statement should not been used here.

As typically overload means same name multiple arguments, but in obj-c we do not follow this. In obj-c overloading is faked by naming the parameters.

So here you have 3 different sets of code, and each one is called some other method. And you do not have a memory leak as you are not allocating memory thrice for same object, instead you are initializing it.

Anoop Vaidya
  • 46,283
  • 15
  • 111
  • 140
  • 1
    Yes you are right. There are no overloads in Objective-C because parameters are part of the method's name thus no two methods can have identical names. – NarbehM Dec 21 '12 at 20:01
  • And rest of your doubts have been sorted out by viki and drummer :) – Anoop Vaidya Dec 21 '12 at 20:08