I am coding in Objective-C / Cocoa-Touch for iPhone, and I was wondering, is it ever OK to not chain initializers? For instance, in my parent class I have three initializers that all need to be inherited from by child class. Is it OK in this instance to not chain? For instance, in this one class I have it set up like this:
- (id)initWithGraphic:(Graphic*)graphic size:(CGSize)size
{
self = [super initWithFrame:CGRectZero];
if (self)
{
...
}
return self;
}
- (id)initWithGraphic:(Graphic*)graphic maximumDimension:(CGFloat)maximumDimension
{
self = [super initWithFrame:CGRectZero];
if (self)
{
...
}
return self;
}
- (id)initWithGraphic:(Graphic*)graphic
{
self = [super initWithFrame:CGRectZero];
if (self)
{
...
}
return self;
}
So essentially I don't have one designated initializer because it's tough for me to chain them together, I basically have three.
Short answer: if you want your code to be understandable and maintainable, then you must always call super's designated initializer.
Long answer...
The -[NSObject init] method is documented to do nothing:
The init method defined in the NSObject class does no initialization; it simply returns self.
Therefore in theory you're not required to call it. In practice it's better to call it because it makes your code more uniform (and therefore easier to understand), and less likely to break if you decide to change a class to inherit from something other than NSObject.
If you have some inheritance chain MyGrandparent > MyParent > MyObject, and you are the implementer of both MyParent and MyObject, and MyParent doesn't override all of MyGrandparent's initializers, then you could directly call one of MyGrandparent's non-overridden initializers from your MyObject initializers. But again, this is a bad idea. It will be confusing when you or someone else has to revisit the code later, and likely to break if you change the implementation of MyParent later.
Also, if you're creating multiple initializers, you need to learn about designated initializers here and here. It's important to pick one of your initializers as the designated initializer, and make sure all subclass initializers call the superclass's designated initializer directly. Otherwise, you're likely to end up creating infinite recursion. I explained this problem thoroughly in this answer and this answer.
UPDATE
In your example, you could set up a designated initializer like this:
static CGFloat const kMaximumDimensionNone = 0; // or NAN if 0 is a valid max dimension
static CGSize const kSizeNone = { 0, 0 }; // or NAN, NAN if 0x0 is a valid size
- (id)initWithGraphic:(Graphic *)graphic size:(CGSize)size maximumDimension:(CGFloat)maximumDimension {
if (self = [super init]) {
_graphic = graphic;
if (!CGSizeEqualToSize(size, kSizeNone)) {
[self configureWithSize:size];
}
else if (maximumDimension != kMaximumDimensionNone) {
// Use if(!isnan(maximumDimension)) instead if you use NAN as the placeholder
[self configureWithMaximumDimension:maximumDimension];
}
else {
[self configureWithDefaultDimensions];
}
}
return self;
}
- (id)initWithGraphic:(Graphic *)graphic {
return [self initWithGraphic:graphic size:kSizeNone maximumDimension:kMaximumDimensionNone];
}
- (id)initWithGraphic:(Graphic *)graphic size:(CGSize)size {
return [self initWithGraphic:graphic size:size maximumDimension:kMaximumDimensionNone];
}
- (id)initWithGraphic:(Graphic *)graphic maximumDimension:(CGFloat)maximumDimension {
return [self initWithGraphic:graphic size:kSizeNone maximumDimension:kMaximumDimensionNone];
}
You don't have to expose the designated initializer in the public header file. You can add a a separate header file exposing the designated initializer for subclasses to import. For example, Apple does this with the UIGestureRecognizerSubclass.h header, which declares the ForSubclassEyesOnly category on UIGestureRecognizer.
Or you could expose the configureWith... methods to your subclasses, and maybe you wouldn't even need to have them override the initializers.
Related
My class A is initialised like that:
- (instancetype)init // designated initializer
{
self = [super init];
if (self) {
[self setup]; // just sets some properties
}
return self;
}
I have the following class B which subclasses A:
- (instancetype)init
{
self = [super init]; // calls the initializer of A and SHOULD call the setup code of A but it does not
if (self) {
[self setup]; // just sets some properties for B
}
return self;
}
My Problem:
B should call A's setup method but it does not due to the dynamic typing of Objective-C. Instead B calls its setup code twice.
How can I resolve this problem (besides renaming the methods) or even better ARE THERE ANY BETTER APPROACHES?
I could move the code written in setup to init but the problem is that I have more than one designated initializer (for example: UIViewControllers has initWithFrame: and initWithCoder:) and I am lazy about writing code twice.
I'm not sure why you reject renaming the methods. It's a simple solution.
Another approach is to use functions instead of methods. Functions are dispatched statically, not dynamically.
For example, in the implementation of A:
static void setup(A* self)
{
self.someProperty = /* ... whatever ... */;
}
- (instancetype)init // designated initializer
{
self = [super init];
if (self) {
setup(self); // just sets some properties
}
return self;
}
Likewise for B.
In a function, self doesn't have any special meaning, so it's just a local variable name. You could use a different one, if you prefer.
Assuming the function is inside the #implementation, it has full access to the object, including to private instance variables. So, setup() could use self->_someProperty instead of self.someProperty.
What about a simple approach? Make setup in B call [super setup] and don't call setup in B.
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?
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 {
...
}
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.
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.
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.
Disclaimer, I'm new to Objective C. But I can't find this explained. I've seen two ways of implementing init:
- (id)init {
if ([super init]) {
return self;
} else {
return nil;
}
}
and
- (id)init {
if (self = [super init]) {
// do your init business here
}
return self;
}
so let's say i have:
myObj = [[MyObject alloc] init];
where MyObject class is a subclass of NSObject. in the second example, does init not return an initialized version of NSObject? so myObj would ... how would it know what it is? wouldn't it think it was an NSObject rather than a MyObject?
1) First version is just wrong. self should be always assigned with value returned by super initializer, because init<...> of super can return another object upon initialization (it's not unusual BTW). Second version is actually an 'official' way to implement init<...> methods.
2) 'wouldn't it think it was an NSObject rather than a MyObject'. myObj is instance of 'NSObject' and instance of 'MyObject'. It's the whole point of inheritance.
i just want to know, under the hood, how it does it.
It's pretty simple. 99.9% of all the classes you'll ever write will inherit from NSObject in some fashion. In the initializers, you're supposed to invoke super's designated initializer and assign it to self. Eventually, [super init] will be invoking -[NSObject init]. According to the documentation, that's implemented like this:
- (id)init {
return self;
}
So technically, if you inherit directly from NSObject, you're probably safe to not do the assignation of self = [super init];, because you know (and you're guaranteed) that this is equivalent to: self = self;, which is kind of pointless. Regardless, you should leave it in for consistency's sake.
However, once you start getting further down the inheritance chain, and especially when you're inheriting from opaque classes (ie, a class whose .m file you do not have), then things start getting shady. It is possible that you'll come across a class whose designated initializer looks something like this:
- (id) initWithFoo:(id)aFoo {
if ([aFoo isSuperFast]) {
[self release];
return [[SuperFastFooWrapper alloc] initWithFoo:aFoo];
}
self = [super init];
if (self) {
_foo = [aFoo retain];
}
}
This isn't as common, but it does happen. In this case, we're destroying self ([self release], to balance the alloc call that immediately preceded this) and instead returning a different object.