Calling an optional method in superclass (Objective-C) - objective-c

I'm trying to create a generic implementation for the NSCoding protocol.
The code will be wrapped around in a macro the will implement NSCoding.
In order to implement the protocol, we need two functions:
-(void)encodeWithCoder:(NSCoder*)coder;
-(id)initWithCoder:(NSCoder*)coder;
A generic implementation of the initWithCoder function would be:
-(id)initWithCoder:(NSCoder*)coder {
if ([super conformsToProtocol:#protocol(NSCoding)])
self = [super initWithCoder:coder];
else {
self = [super init];
}
if (!self) return self;
self = [MyGenericCoder initWithCoder:coder forObject:self withClass:[__clazz class]];
return self;
}
The problematic line is self = [super initWithCoder:coder]; it will not compile since super does not respond to initWithCoder: when we use the in a class that its super does not implements NSCoding. Casting super to NSObject<NSCoding>* will not work with the LLVM compiler.
[super performSelector:(initWithCoder:) withObject:coder] will not work either since super == self, Which will result in an infinite loop.
How can I call [super initWithCoder:coder] in manner that will trigger the function in the superclass and will not generate a compilation warning / error?

You can use +instancesRespondToSelector: to find out if your superclass responds to the selector, and then objc_msgSendSuper() directly to actually call it.
#import <objc/message.h>
- (id)initWithCoder:(NSCoder *)coder {
// Note: use [__clazz superclass] directly because we need the
// compile-time superclass instead of the runtime superclass.
if ([[__clazz superclass] instancesRespondToSelector:_cmd]) {
struct objc_super sup = {self, [__clazz superclass]};
((id(*)(struct objc_super *, SEL, NSCoder*))objc_msgSendSuper)(&sup, _cmd, coder);
} else {
[super init];
}
if (!self) return self;
self = [MyGenericCoder initWithCoder:coder forObject:self withClass:[__clazz class]];
return self;
}

How can I call [super initWithCoder:coder] in manner that will trigger the function in the superclass and will not generate a compilation warning / error?
Just create two variants of your macro -- one for types whose superclasses adopt NSCoding, and another for those which do not.
Either that, or abstract the detail from yourself and derive from intermediate types which abstract the condition from your bases and adopts NSCopying -- then you can just call initWithCoder: on any such type.

#import <objc/runtime.h>
-(id)initWithCoder:(NSCoder*)coder {
Class superclass = class_getSuperclass([__clazz class]);
SEL constructor = #selector(initWithCoder:);
if (class_conformsToProtocol(superclass,#protocol(NSCoding))) {
self = class_getMethodImplementation(superclass,constructor)(self,constructor,coder);
}
else {
self = [super init];
}
if (!self) return self;
self = [MyGenericCoder initWithCoder:coder forObject:self withClass:[__clazz class]];
return self;
}

Related

Designated initializer should only invoke a designated initializer on 'super'

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];

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

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.

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 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

Objective C init

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.