Designated initializer should only invoke a designated initializer on 'super' - objective-c

This code is a modified version of Ordered Dictionary implemented here.
https://github.com/nicklockwood/OrderedDictionary/tree/master/OrderedDictionary
Interface -> OrderedDictionary.h
#interface OrderedDictionary : NSMutableDictionary
{
}
Implementation -> OrderedDictionary.m
// This is the new method added.
- (instancetype)init
{
return [self initWithCapacity:0];
}
- (instancetype)initWithCapacity:(NSUInteger)capacity
{
self = [super init];
if (self != nil)
{
// Allocate here.
}
return self;
}
The code works fine but I get following warnings in "- (instancetype)init".
Designated initializer should only invoke a designated initializer
on 'super'
Designated initializer missing a 'super' call to a
designated initializer of the super class
What am I doing wrong and how do I fix it?
Made following changes to the code to fix the problem
// This is the new method added.
- (instancetype)init
{
self = [super init];
if (self != nil)
{
// Allocate here.
}
return self;
}
- (instancetype)initWithCapacity:(NSUInteger)capacity
{
self = [super initWithCapacity:capacity];
if (self != nil)
{
// Allocate here.
}
return self;
}

try adding
- (instancetype)initWithCapacity:(NSUInteger)capacity NS_DESIGNATED_INITIALIZER;
To the #interface block. All initialization paths should flow through the designated initializer - in your case initWithCapacity: is the obvious choice since init calls it. You may still run into problems, the designated initializer must call the super class's designated initializer. If the super class defines designated initializers those must be implement by your class as well...
In general subclassing NSMutableDictionary is considered bad practice because NSMutableDictionary is the public facing piece of a class cluster. This may be part of the cause of the compiler warnings.

As you can read in NSMutableDictionary documentation, there are two designated initializer for this class:
initWithCapacity: Designated Initializer
init Designated Initializer
Here you are calling from initWithCapacity in your class to super.init. That's the reason the compiler warns you.
This code maybe is better:
// This is the new method added.
- (instancetype)init
{
return [self initWithCapacity:0];
}
- (instancetype)initWithCapacity:(NSUInteger)capacity
{
self = [super initWithCapacity:capacity];
if (self != nil)
{
// Allocate here.
}
return self;
}

I'd bet some of you are tripping over this warning message when you have Xcode convert your code to "Modern Objective-C Syntax."
I've seen cases where Objective-C header files have been modified by the converter to have two designated initializers:
- (instancetype) init NS_DESIGNATED_INITIALIZER; // DO NOT USE
- (instancetype) initWithFileset: (NSArray *) fileset NS_DESIGNATED_INITIALIZER; // use this to instantiate
If you modify this code to a single initializer, you may find the warning message goes away:
- (instancetype) init; // DO NOT USE
- (instancetype) initWithFileset: (NSArray *) fileset NS_DESIGNATED_INITIALIZER; // use this to instantiate
In short, check the header and the source file when you see this warning message.

You should only call the super of your own function.
So replace
self = [super init];
with
self = [super initWithCapacity:capacity];

Related

Objective-c - what does [super init] return?

New to objective-c. Wrote a code-snippet to better understand the init mechanism, and ended up with a few questions.
#implementation MyClass
-(id) init
{
if (self) {
i = 5;
NSLog(#"self before init - %# %p i=%d",[self className], &self, i);
} else {
NSLog(#"self is null???");
}
id someClass = [super init];
NSLog(#"the result of super-init - %# %p",[someClass className], &someClass);
self = [super init];
if (self) {
NSLog(#"self after init - %# %p %d",[self className], &self, i);
} else {
NSLog(#"self is null???");
}
return self;
}
i is a private instance variable int.
Here is the result:
2012-12-14 18:01:26.403 Init[1621:303] self before init - MyClass 0x7fff5fbff848 i=5
2012-12-14 18:01:26.405 Init[1621:303] the result of super-init - MyClass 0x7fff5fbff838
2012-12-14 18:01:26.405 Init[1621:303] self after init - MyClass 0x7fff5fbff848 5
What really surprised me is that that someClass's class Name is MyClass.
How does NSObject know to return the sub-classes instance (not just the type match, it is the exact same object)?
I'm aware that it is not good form to call init many times, and initialize instance variables before calling init but I was just experimenting.
Thanks.
You do need to use the standard schemen (more or less):
-(id)init {
self = [super init];
if (self) {
// Do initialization stuff
}
}
Your class subclasses some other class. The call to super init runs the init routine of your superclass. Without it your class is not properly initialized and may malfunction strangely. (However, it's probably not wise to call super init twice, as this could have bad side-effects.)
There are cases where you would not call super init, but would instead call a version of init in your own class. Basically, if you have initWithJunk: and init, you can have initWithJunk: call [self init] instead of [super init] so that the stuff that self init would do gets done and doesn't have to be reproduced in initWithJunk:.
This is especially critical if you write a "category" that adds an init... method to an existing class -- you must call some version of [self init] to assure that the base class's initializer runs.
Understand that the super init method is not (usually) replacing the existing instance with a new one, but rather is initializing instance fields in it that belong to the superclass. The reason for receiving the "self" value back from the super init call is two-fold:
The init routine can return a nil in the event that some sort of error occurs.
In some (rare) special cases the init routine may replace the supplied instance with a different one (eg, a cached version).

Preventing subclasses overriding methods

Assume there is an object that initialises like so
- (void)doInit
{
NSLog(#"In BaseClass init");
}
- (id)init
{
self = [super init];
[self doInit];
return self;
}
and it has a subclass which is inited in a similar way
- (void)doInit
{
NSLog (#"In SubClass init");
}
- (id)init
{
self = [super init];
[self doInit];
return self;
}
Now if I create an instance of child class then I receive the following output:
In SubClass init
In SubClass init
when really, what I meant to happen is
In BaseClass init
In SubClass init
Is there a way to mark doInit to say that it shouldn't be overridden or do I need to create a unique name for all methods in a subclass?
I'm not entirely sure how I haven't come across this issue before, but there you go.
Edit:
I understand why this is happening, I hadn't expected that the base class would be able to call the overridden function.
I also can't just call [super doInit]; from the Subclass method because the BaseClass still needs to call doInit so that creating an instance of BaseClass will still work. If I called [super doInit], I'd still end up getting SubClass's doInit called twice.
It appears the answer is no and I'll just have to uniquely name each doInit like doBaseClassInit and doSubClassInit.
If you have a method that you don't want to by dynamically bound (i.e. don't want a subclass method to be called if it exists), you need to do it as a C function instead. So, you could do this instead:
In the base class:
static void DoInit(BaseClass *self)
{
NSLog(#"In BaseClass init");
}
- (id)init
{
self = [super init];
if (self) {
DoInit(self);
}
return self;
}
in the subclass:
static void DoInit(SubClass *self)
{
NSLog(#"In SubClass init");
}
- (id)init
{
self = [super init];
if (self) {
DoInit(self);
}
return self;
}
Note that both the DoInit methods are marked as static, so they are only visible each compilation unit (.m file) and don't conflict with each other.
You could, perhaps, try something like this in your base class. It would mean any time the init implementation inside BaseClass executed, the doInit implementation for BaseClass would be called.
- (void)doInit
{
NSLog(#"In BaseClass init");
}
- (id)init
{
self = [super init];
Class baseClass = [BaseClass class];
SEL selector = #selector(doInit);
IMP baseClassImplementation = class_getInstanceMethod(baseClass, selector);
baseClassImplementation(self, selector);
return self;
}
As I mentioned in my comment, if that's the narrowness of your need this should work as it gets around the dynamic method lookup involved with sending a message. Hope this helps!
EDIT:
Disclaimer - if you're in this situation it's probably not a good sign for the longevity of your design. This technique will get you up and running for now but please document it carefully, and consider ways to refactor your code so this is no longer used. Consider fixes like these to really be used only when extremely urgent.
The reason why you are not getting the "In BaseClass init" console message is because your subclass is not calling the super's implementation of doInit.
If you don't want doInit overridden the 'best' way to avoid doing so is to not publish the existence of this method. Remove it from your header and uniquely name the method so that a collision is unlikely. For example, many of the private methods in Apple's frameworks have a leading underscore. So, for example, you could call your method _doInit and it will be very unlikely that a subclass accidentally create it's own overiding implementation.
Nope, there's no enforceable way to prevent a subclass from overriding a method. The best you can do is to avoid putting it in the public header file for the class so someone is not likely to try to override it. If the method has to be public, you just put a note in the documentation for the method that it shouldn't be overridden or that subclasses should call super's implementation whichever the case may be. You'll find these kind of instructions all over in the documentation for Apple's own classes.
If you want your subclass to use the baseclass version of doInit then in the subclass don't declare a doInit method. Here's what I mean:
ClassA:
#interface ClassA :
-(void) doInit;
-(id) init;
#implementation
-(void) doInit {
NSLog(#"ClassA doInit");
}
-(id) init {
self = [super init];
if (self != NULL)
[self doInit];
return self;
}
ClassB
#interface ClassB : ClassA
-(id) init;
#implementation
-(id) init {
self = [super init];
if (self != NULL)
[self doInit];
return self;
}
And really you don't need to override the init method as well unless there's some special code that you want that class to do.

How do you perform additional initialization after calling the designated initializer in Objective-C? (self = [self...)

Suppose I have a designated initializer that does some initialization as such:
- (id)initWithBlah:(NSString *)arg1 otherBlah:(NSArray *)arg2
{
if (self = [super init])
{
...
}
return self;
}
and I have another initializer that needs to call this, but then perform some other setup task:
- (id)initWithSomeOtherBlah:(void *)creativeArg
{
// Is this right? It seems to compile and run as expected, but feels wrong
self = [self initWithBlah:nil otherBlah:nil];
if (self)
{
[self someProcessingForThisInitDependentOnSelfInit:creativeArg];
}
return self;
}
Since the test to make sure the return value is correct, should 'self' be used in this context? I'm wondering if this is even a valid combination of events. We have a situation where we have an initializer that needs to perform some additional setup after the designated initializer is ran.
I'm wondering if the correct approach is to shove this additional processing in the designated initializer..
Please let me know if more clarification is needed. I was trying to keep this simple. :)
Thanks!
A general rule of thumb that I follow is that the designated initializer is the initializer with the most parameters and the other initializers chain down to the designated initializer.
In your example you are not using creativeArg in your initWithSomeOtherBlah constructor. I am not sure if that was intentional or not.
With this approach you are being explicit with your intentions when creating an object instead of side effect programming.
For example:
#implementation BlaClass
- (id)initWithBlah:(NSString *)arg1 otherBlah:(NSArray *)arg2 creativeArg:(void *)arg3
{
if (self = [super init])
{
self.arg1 = arg1;
self.arg2 = arg2;
self.arg3 = arg3;
[self someProcessingForThisInitDependentOnSelfInit:arg3];
}
return self;
}
- (void)someProcessingForThisInitDependentOnSelfInit:(void *)creativeArg
{
if(creativeArg == NULL) return;
//do creative stuff
}
- (id)initWithSomeOtherBlah:(void *)arg
{
return [self initWithBlah:nil otherBlah:nil creativeArg:arg];
}
...
#end
If you need two initializers in your class which initialize the class somewhat differently, a good coding practice is to identify the setup tasks that both initializers need to perform them, and move them to a separate method. This way, you don't need to call one custom initializer inside another. Here is how you need to do it:
-(void) setupBlah
{.....}
- (id)initWithBlah:(NSString *)arg1 otherBlah:(NSArray *)arg2
{
if (self =[super init])
{
[self setupBlah];
//Do other initialization
....
}
return self;
}
- (id)initWithSomeOtherBlah:(void *)creativeArg
{
if (self = [super init])
{
[self setupBlah];
//Do other initialization
.....
}
return self;
}
There is nothing wrong with calling another initializer from a non-designated initializer, see Apple's docs here.
In the case that I have two or more designated initializers (such as a view with initWithFrame: and initWithCoder:), I have found myself to define a setUp method that I call from both initializers, which is just a shorter name for your someProcessingForThisInitDependentOnSelfInit method.

Designated initializer and initWithFrame

I've created a class with designated initializer defined that way:
-(id)initWithFrame:(CGRect)frame
aProperty:(float)value {
self = [super initWithFrame:frame];
if(self){
///....do something
}
}
Now I wonder how to avoid direct calls to initWithFrame method, like:
MyClass *theObj = [MyClass alloc]initWithFrame:theFrame];
I thought about throwing exception in initWithFrame definition:
- (id)initWithFrame:(CGRect)frame
{
#throw ([NSException exceptionWithName:#"InitError"
reason:#"This class needs to be initialized with initWithFrame:aProperty: method"
userInfo:nil]);
}
Is this a good solution ?
What about calling your designated initializer with a default value for aProperty ?
Two techniques I use with initializers. I'm sure there a more.
Technique 1:
In initWithFrame call your initializer with a default value for a Property
- (id)initWithFrame:(CGRect)frame
{
return [self initWithFrame:frame aProperty:1.0f];
}
Technique 2:
Simply return nil;
- (id)initWithFrame:(CGRect)frame
{
return nil;
}

How to write an Objective-C convenience constructor

I'm trying to add a convenience constructor to my custom object.
Similar to [NSArray arrayWithArray:]
I know it involves a class method that returns an auto released object. I've been googling around but all I can seem to find is the definition of a convenience constructor but not how to write one.
Let's say you have the following:
#class PotatoPeeler : NSObject
- (instancetype)initWithWidget: (Widget *)w;
#end
Then to add a factory method, you'd change it to this:
#class PotatoPeeler : NSObject
+ (instancetype)potatoPeelerWithWidget: (Widget *)w;
- (instancetype)initWithWidget: (Widget *)w;
#end
And your implementation would simply be:
+ (instancetype)potatoPeelerWithWidget: (Widget *)w {
return [[[self alloc] initWithWidget: w] autorelease];
}
Edit: replaced id with instancetype. They are functionally identical, but the latter provides better hints to the compiler about the method's return type.
Generally my approach is the following: first I create a normal initializer method (instance method), then I create a class method that calls the normal initializer. It seems to me Apple uses the same approach most of the time. An example:
#implementation SomeObject
#synthesize string = _string; // assuming there's an 'string' property in the header
- (id)initWithString:(NSString *)string
{
self = [super init];
if (self)
{
self.string = string;
}
return self;
}
+ (SomeObject *)someObjectWithString:(NSString *)string
{
return [[[SomeObject alloc] initWithString:string] autorelease];
}
- (void)dealloc
{
self.string = nil;
[super dealloc];
}
#end