Should subclasses call the designated initializer in the immediate super class? - objective-c

I've seen some sample code which has got me wondering about calling the designated initializer in the super classes. Say I have some code this:
#interface NewTableViewCell : UITableViewCell {
}
#end
#implementation NewTableViewCell
- (id) initWithFrame: (CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Do some stuff
}
return self;
}
#end
Note that initWithFrame is the designated initializer for UIView, not UITableView. Should this code always be calling [UITableViewCell initWithStyle:reuseIdentifier:], or does it depend on the coder's intent?

When subclassing, the guideline is that the designated initializer has to call its super class' designated initializer.
Another guideline is that the subclass needs to override the superclass' designated initializer to call the new designated initializer.
If UITableViewCell follows this guideline (and it does; I tested with the help of a category), it overrides its superclass' designated initializer (UIView's initWithFrame:) to call the new designated initializer (initWithStyle:reuseIdentifier:). Therefore, if you call initWithFrame: on UITableViewCell it will call initWithStyle:reuseIdentifier:, which in turn will call initWithFrame: on super (UIView).
Therefore, it will need an additional method call but it will eventually go through initWithStyle:reuseIdentifier:.
Again, the best practice is that the designated initializer has to call the super class' designated initializer and any other initializer that isn't the designated initializer has to call the designated initializer. From "The Designated Initializer":
General principle: The designated initializer in a class must, through a message to super, invoke the designated initializer in a superclass.
Designated initializers are chained to each other through messages to super, while other initialization methods are chained to designated initializers through messages to self.

I agree it depends on the coders attempt but the coder should always try and use the designated initializer. Think about initializers you may have written, they probably do additional work for your object to be in a usable or desired state. If you are overriding an initializer like you are doing in your example you should call the overridden initializer as well. If that was a custom init method then you would want to call the designated initializer because for UITableViewCell's that is the only way to set the reuseIdentifier publicly.
//Override initWithFrame
//Fine although it may not (should not) get called for a UITableViewCell
- (id) initWithFrame: (CGRect)frame {
self = [super initWithFrame:frame];
//Design a custom initializer to gather parameters for supers default initializer
-(id)initWithCustomObject:(id)object style:(UI..Style)style reuseIdentifier:(NSString*)rid {
//This should call initWithStyle:reuseIdentifier:

Related

Beginner Objective-C: Extending a class?

I'm very new with objective-C, and I am trying to implement a 'factory' style class extending a cocos2D class called CCSprite.
I want there to be a class Monster which extends CCSprite that will create and issue sprites with unique IDs so that I may call and manipulate the generated sprites later on; I am extending CCSprite to add an instance_id property and a static global_id variable.
I want to be able to use all the CCSprite constructors such as spriteWithImageNamed along with all the other functions CCSprite has, the problem is that my instance_id isn't being assigned/incremented properly(It's always 0), and I don't really know where to start to fix this.
Here is what I have:
Monster.h:
#import "CCSprite.h"
#interface Monster : CCSprite
#property (nonatomic, readonly) int instance_id;
#end
Monster.m:
#import "Monster.h"
static int global_id = 0;
#implementation Monster:CCSprite
#synthesize instance_id;
-(id) init{
self = [super init];
if(self){
instance_id = global_id;
global_id++;
}
return self;
}
#end
Is the init function called every time any constructor is called? (eg spriteWithImageNamed)?
How do I ensure my extended properties and variables are applied when I call the parent class's functions?
In Objective-C, a class's designated initializer is the one that other initializers are supposed to call into. For many classes, this is indeed init. According to the Cocos2D documentation, however, init is not the designated initializer for CCSprite. Instead, the designated initializer is:
- (id)initWithTexture:(CCTexture *)texture rect:(CGRect)rect rotated:(BOOL)rotated
So that is the method you need to override if you want all the other initializers to call your version.
(If you want to confirm for yourself that this works, you can put a log statement or breakpoint in your implementation of the designated initializer, then call other initializers and make sure the designated initializer is called.)
Your init method may or not be called depending on what init these factory methods are using internally. For example, initWithFile may call self = [super init] instead of self = [self init], in which case your implementation will not be called. You can ensure that your initialization is always performed by overriding all init methods of all superclasses and do your necessary setup. Also keep in mind that you can also override any of the factory methods, where you can get the object from calling super, and then assign it's id property before returning.
looking at CCSprite it seems that every factory method like +(id)spriteWith* calls an initWith* method
and every initWith* method at the end of the chain calls [self init]
so if you override the init method as you do in monster.m you are ok!
by the way, if you want to create your sprite using a factory method do not call [CCSprite spriteWith*], call [Monster spriteWith*] instead
best regards

Initializing a class with designated initializer - confusion

I am reading a book which teaches that we should implement a designated initializer of class, and then let other initializers call this one (which I agree with).
Now, I am in a section in this book, which says: "The designated initializer of UITableViewController is initWithStyle:". And then goes in the implementation like this:
// inside ItemsViewController.m, subclass of UITableViewController
-(id) init{
// Call the superclass's designated initializer
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
}
return self;
}
- (id)initWithStyle:(UITableViewStyle)style
{
return [self init];
}
After this code, the book also says: "This will ensure that all instances of ItemsViewController use the UITableViewStyleGrouped style, no matter what initialization message is sent to it."
To stick to the principle I described in the beginning of the post, I might had implemented this class like this:
// inside ItemsViewController.m, subclass of UITableViewController
// Implement the designated initializer first
-(id) initWithStyle:(UITableViewStyle)style
// Call the superclass's designated initializer
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
}
return self;
}
// Call designated initializer
- (id)init
{
return [self initWithStyle: nil];
}
But I think I am missing something? I think the book uses init as designated initializer in their case?
Construction
// Implement the designated initializer first
-(id) initWithStyle:(UITableViewStyle)style
// Call the superclass's designated initializer
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
}
return self; }
is confusing for user of class, because he will think that you provided initWithStyle constructor to allow him setting controllers' style. You should use your first init method.
If you have custom tableviewcell class then you dont need to implement initwithstyle initializer and also that initialilzer will not be called for primitive and class member objects.you will have to suffer.so follow the standard initialiser.
Basically your code and the code in the book do the same thing. there are different types of initializers for classes, you use the one you need. What the example in the book wants to show you is how to overwrite them if you need some custom initializers later on. Sow it's understandable that it doesn't really make sense from a real live application point of view but from a learning overwriting initializers and customization i think it's ok.

Override designated initializer of superclass

I am reading a book which has a guideline:
"If a class declares a designated initializer that is different from its superclass, the superclass’s designated initializer must be overridden to call the new designated initializer"
As I understand this guideline in other words is that, if I am subclassing my class form its superclass, and my subclass has a designated initializer which is different from des. initializer of its superclass, then in my subclass I must override the designated initializer of my superclass and inside it call the designated initializer of my subclass.
Is this true? Do we have to do this all the time? Thank you.
#justin is basically on the point.
Methods in Objective-C are inherited. That means if the superclass has an initializer method (initializers are just methods), and your subclass does not override it, then your subclass will inherit that superclass's initializer method. That means that people can always call that superclass's initializer on an object of your subclass (basic consequence of inheritance and subtype polymorphism). But that might not be what you expected. The superclass's initializer might not do all the initialization that your class needs.
That's why you should override the superclass's initializer. If you don't want people to use that initializer on an object of your class, you should throw an exception in that initializer. Otherwise, you should override it to do any appropriate initialization for your class.
Is this true? Do we have to do this all the time?
Personally, I consider it a bad guideline. It is illogical to implement the superclass' designated initializer (to do anything meaningful) when you have specified a stricter designated initializer (e.g. one which introduces a parameter).
For example, -initWithDomain:code:userInfo: is NSError's designated initializer; Could [[NSError alloc] init] return a reasonably descriptive error?
If anything, privately override the 'deleted' initializer and treat it as an programmer error to call, but do not pretend that it is acceptable for a client to use an initializer other than a designated initializer.
Note that your class will in some cases be able to support both initializers. In that case, just redeclare in your #interface's designated initializers. This is sufficient to document a designated initializer. Either that, or document an initializer or set of initializers as designated initializers, which would logically invalidate any superclass' designated initializers.
Of course, your initializer should call one of the superclass' designated initializers in its initialization.
Ex.1:
// implicitly adds a designated initializer. -init is still valid:
#interface MONObject : NSObject
- (instancetype)initWithString:(NSString *)pString;
#end
Ex.2:
// redefines the designated initializer. -init is not valid:
#interface MONObject : NSObject
// MONObject's designated initializer
- (instancetype)initWithString:(NSString *)pString;
#end
Ex.3:
// define all designated initializers:
#interface MONObject : NSObject
// MONObject's designated initializers:
- (instancetype)init;
- (instancetype)initWithString:(NSString *)pString;
#end
EDIT
Question clarified in comments.
When you are simply overriding an initializer declared by the superclass:
Is this true? Do we have to do this all the time?
Unless your class has initialization to perform, you do not need to explicitly override the superclass' designated initializer.
Your instance will be initialized to have zeroed memory.
Given:
#interface MONObject : NSObject
- (instancetype)initWithString:(NSString *)pString;
#property (nonatomic, copy, readwrite) NSString * string;
#end
#implementation MONObject
// if #property string should be initialized to nil, you may omit -[MONObject init]
// otherwise, initialize self here:
- (instancetype)init
{
// call super's designated initializer:
self = [super init];
// test it:
if (nil == self) return nil;
// init your state
_string = #"(null)";
return self;
}
- (instancetype)initWithString:(NSString *)pString;
{
// call super's designated initializer:
self = [super init]; // << will not call -[MONObject init]
// test it:
if (nil == self) return nil;
// init your state
_string = pString.copy;
return self;
}
#end
It's basically saying that if a class has an iniWithSomethingDomething, then is preferred to do a
self = [super initWithSomethingSomeThing:......]
in your own initializer
I as understand it, If your class has a designated init, you want to override the supers init so it calls your designated init.
in your implementation kind of like so.
make your designated init
-(id) initWithName:(NSString *)aName
{
self = [super init];
if (self){
[self setName:aName];
}
return self;
}
then call it when overriding the supers
-(id) init
{
return [self initWithName: #""];
}

Is NSObject init always called?

In Cocoa, it's recommended to always called the superclass's designated initializer. Is it safe to assume that init will always be called, during an object's initialization, even if the object isn't initialized with init?
For example, if let's say there's a class: NSThingie, which can be initialized like [[NSThingie alloc] initWithFoo: foo], or [[NSThingie alloc] initWithFoo: foo andBar: bar]. If I override init, can I assume that it will be called at some point during initialization?
EDIT
Maybe my original question wasn't worded so well. I know that overriding init causes init in superclasses to not be called, and I must explicitly call init. What I'm wondering is whether, in the apple frameworks, [NSObject init] is always called. If I initialize some Apple object like [[NSAppleClass alloc] initWithSomething: something], can I assume that NSObject's init will eventually be called during the initialization of NSAppleClass?
short answer: no
you need to make sure you are overriding the correct init method for the class you are using
for example UIView has an init method initWithFrame: and a very basic implementation of that would be:
- (id)initWithFrame:(CGRect)frame
{
self = [super init]; //calls init because UIResponder has no custom init methods
if (self){
self.frame = frame;
}
return self;
}
init is called on the superclass, but not on UIView, so if you were to override init and not initWithFrame: your initialisation code would never be run
If I override init, can I assume that it will be called at some point
during initialization?
If you override and init method you are responsible for calling the designated initializer. It is the responsibility of the developer to call correct initializer of the class that is subclassing. The reason you call the designated initializer is to make sure the class is constructed in its intended state. Not calling the correct initializer or not calling an initializer at all will likely result in undefined/undesirable behavior.
yes the "init" default constructor don't do a spécifique processing
because the init method defined in the NSObject class does no initialization; it simply returns self.

Why override the designated initializer of super class?

I was reading the book "Cocoa Design Pattern" and 2 of its point, in chapter 3 (Two-Stage Creation) are making me confused.
Make sure that the superclass’ Designated Initializer is overridden to call the new Designated Initializer.
When subclassing, make sure every new initializer that isn’t the Designated Initializer calls the Designated Initializer.
My question is how we can call the method for which we don't have the parameters to pass? The book example is being posted below. In this method writer has passed some "static" values, but are we supposed to do this? Or is this always desirable?
My second question is, why I have to override the designated method of super class when I will never call it when I will be initializing my object, other than in my own designated initializer, where I will not be passing any parameters (e.g; in case of NSObject)
#interface MYCircle : NSObject {
NSPoint center;
float radius;
}
// Designated Initializer
- (id)initWithCenter:(NSPoint)aPoint radius:(float)aRadius;
#end
#implementation MYCircle
// Designated Initializer
- (id)initWithCenter:(NSPoint)aPoint radius:(float)aRadius {
self = [super init];
if(nil != self) {
center = aPoint;
radius = aRadius;
}
return self;
}
#end
// Overriden inherited Designated Initializer
- (id)init {
static const float MYDefaultRadius = 1.0f;
// call Designated Initializer with default arguments
return [self initWithCenter:NSZeroPoint radius:MYDefaultRadius];
}
Please also help me to correct my question because I am not sure what I am really asking is a correct question.
Thanks.
The designated initializer is the one that properly configures the object. If you don't choose one init... method as the designated initializer, then you have to make sure that every init... method does the right thing. That generally means that they all have to have the same code, or they all have to call a common setup method. It also means that anyone subclassing your class has to override all the init... methods instead of just one.
By picking (i.e. "designating") one init... method as the common method that all the others call, you give subclasses a single override point and a single method that their own init... methods can call to ensure that the superclass is properly configured.
If you don't have the data necessary to call the designated initializer, then you don't have the data required to set up the superclass. Somestimes you can choose reasonable default values, as above, but if not then it doesn't make any sense to create the object at hand.