Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objective-C: How to avoid unintended calls to private superclass methods with identical name?

Tags:

objective-c

I'm trying to avoid duplicate code in various -(id)init flavors of a class, i.e. init, initWithFrame, initWithCoder, etc. by defining a private method that is named commonConstruct.

This method does the heavy lifting common to all init method flavors and gets called by the init constructor.

The issue i have now is that in derived classes, using the same naming convection for the initializer helper ("commonConstruct") the base class will call the derived class's commonConstruct, though it is invisible, i.e. declared in the .m file, not in the .h file.

However, the runtime finds the overloaded commonConstruct and executes that instead of its own member function.

Is there any other way than using a different name for the initializer helper in each subclass ?

In other words: Is there a way of making Objective-C member functions that are "non-virtual", i.e. don't have late (runtime) but compile-time binding?

like image 345
Arasch Honarbacht Avatar asked Jun 20 '13 08:06

Arasch Honarbacht


2 Answers

There's no good compiler-enforced way to do this. Methods are always “virtual” and there is no enforcement of “private” methods.

The common solution is to embed the class name in the method name, thus:

@implementation Thing

- (instancetype)init {
    if (self = [super init]) {
        [self Thing_commonInit];
    }
    return self;
}

- (instancetype)initWithArg:(NSObject *)arg {
    if (self = [super init]) {
        [self Thing_commonInit];
        [self doSomethingWithArg:arg];
    }
    return self;
}

- (void)Thing_commonInit {
    ...
}
like image 155
rob mayoff Avatar answered Nov 15 '22 06:11

rob mayoff


In addition to Rob Mayoff's solution (which I use as well) you could use a static C function:

@implementation Thing
{
    id _privateIvar;
}

- (id)init
{
    return commonInit([super init], nil);
}

- (id)initWithArgument:(id)argument
{
    return commonInit([super init], argument);
}

static Thing *commonInit(Thing *self, id argument)
{
    if (self == nil)
        return nil;
    self->_privateIvar = argument;
    return self;
}

@end

Static functions don't emit symbols, so there are no possible conflicts. You can name all of your common initializers commonInit.

like image 22
Nikolai Ruhe Avatar answered Nov 15 '22 06:11

Nikolai Ruhe