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: #""];
}
Related
I have the following structure.
I got class B which conforms to protocol A.
protocol A defines a designated initializer which is-(instancetype)initWithInt:(int)count.
However, when I go an implement the standard -(instancetype)init in class B and make it to use the designated initializer which is also implemented in class B, i am getting the warning "Designated initializer should only invoke a designated initializer on 'super'", while my designated initializer (which IMO is initWithInt) never calls any designated initializer on super.
#protocol A
{
(instancetype) init;
(instancetype) initWithInt:(NSUInteger)count;
}
#interface B : NSObject <A>
#implementation B
- (instancetype) init {
return [self initWithInt:0];
}
- (instancetype) initWithInt:(NSUInteger)count {
self = [super init];
return self;
}
Any idea why the compiler omits this warning in this specific case?
From Working with Protocols:
A class interface declares the methods and properties associated with that class. A protocol, by contrast, is used to declare methods and properties that are independent of any specific class.
Each class has its own (inherited) designated initializer. You can't declare a designated initializer in a protocol. If you want to declare an initializer in a protocol, implement it like:
- (instancetype)initWithInt:(NSUInteger)count {
self = [self initWithMyDesignatedInitializer];
if (self) {
// do something with count
}
return self;
}
or like:
- (instancetype)initWithInt:(NSUInteger)count {
return [self initWithMyDesignatedInitializer:count];
}
And don't declare init in your protocol, it is declared by NSObject.
Edit:
It doesn't make sense to declare an initializer in a protocol. When you allocate and initialize the object, you know the class of the object and should call a designated initializer of this class.
Edit 2:
A designated initializer is specific to a class and declared in this class. You can initialize an instance of a class but you can't initialize an instance of a protocol. A protocol makes it possible to talk to an object without knowing the class of this object. Documentation about initializers: Multiple Initializers and the Designated Initializer.
MySubClass.h
#interface MySubClass : NSObject
{
}
MySubClass.m
#interface MySubClass ()
#end
#implementation MySubClass
-(MySubClass*)initMySubClass:(id)view{
// initialize my SubClass
}
#end
What is the proper way to write a custom initializer if I subclass from NSObject?
Do I need to use "init" inside my initializer?
-(instancetype)initMySubClass:(id)view {
if(self = [super init]) {
// custom initialization here
}
return self;
}
Apple docs are here: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/index.html#//apple_ref/occ/instm/NSObject/init
Note that, for NSObject itself, the init method just returns self. It is still best practice to call init, however. For example, what if Apple were to include additional initialisation there in the future? Or, what if you want to change the superclass?
#michaelrccurtis's answer is correct. But here's more thorough documentation on how you generally do initialization in Obj-C.
Basically, every class can have a designated initializer and if you subclass it, you must override that initializer and call super from your subclass's designated initializer. The graphic in the docs makes this clearer.
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.
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:
I have three Objective-C classes:
#interface ClassA : NSObject{
IBOutlet id<ClassAProtocol>delegate
//other instance variables
}
//methods
#end
#interface ClassB : ClassA{
//instance variables
}
//methods
#end
#interface ClassC : ClassA{
//instance variables
}
//methods
#end
My objective is so that when an instance of ClassA is called for in either code or InterfaceBuilder, an instance of ClassB or ClassC is actually created. Whether it will be ClassB or ClassC depends on a return value of a method in ClassAProtocol as implemented by the delegate object.
#implementation ClassA
static BOOL _initFromSubclass = NO;
-(id)init{
if(_initFromSubclass){
_initFromSubclass = NO;
self = [super init];
}else {
_initFromSubclass = YES;
if([delegate shouldInitClassB]){
self = [[ClassB alloc] init];
}else{
self = [[ClassC alloc] init];
}
}
return self;
}
//other methods
#end
This doesn't work the way I wanted because at the init call, the delegate (set in Interface Builder) is still nil, and so the object created is always ClassC. Also, a ClassA object is created first, then, in its init call, creates a new ClassC object, with a different memory address, and no ClassA object is dealloced. I have three questions:
1)What happens to the original ClassA object? (I think it's leaked, but I want to know).
2)How do I avoid the leak?
3)How do I accomplish what I actually want? By the time the delegate is set (say, in awakeFromNib method), it's too late to reset the object.
Yes I think it will be leaked because it has a retain count of +1 after the alloc but nothing will release it.
You can use [self release] before reassigning the value of self. Some argue that it should be [self dealloc] directly, but I personally prefer the former.
Objects instantiated from nibs are sent initWithCoder: messages I think, so override that instead of init. Although, at this point, I'm still not sure whether delegate will be set.