Anti-pattern in object-oriented programming
You should be able to do this:
Method method = class_getInstanceMethod([ParentParentClass class], @selector(myMethod));
IMP imp = method_getImplementation(method);
((void (*)(id, SEL))imp)(self, @selector(myMethod)); // cast the function to the correct signature
You may need to #import <objc/runtime.h> in order for this to compile.
This gets the actual C function that the method is translated to at compile time, which you can then call. When the Objective-C compiler compiles your code, all methods are translated into plain C functions, which take self as their first argument, and _cmd, the selector for the current method as the second argument, followed by all of the other arguments that the Objective-C method takes. class_getInstanceMethod obtains the runtime representation of the given method (including various metadata), and method_getImplementation gets the plain C function pointer from that method.
If you look at the Objective-C runtime header, you'll see that the IMP type is defined as typedef id (*IMP)(void);, so you need to cast it to the actual type of the method implementation function, which will be (return_type (*)(id, SEL, method_arguments_in_order)) — the function takes self and the method selector as its first two arguments, followed by the ObjC method parameters.
So, once you have the standard C function pointer, you can simply call it as you would call a function.
I wouldn't go so far as to call this approach hacky, but it certainly is non-standard, as made clear by the need to use the underlying runtime methods directly to achieve what you want. I would definitely consider this a better solution, in terms of design, reliability and making sense, than adding bridging methods in the superclass that call its superclass' methods.
super isn't an object, it's a special keyword that tells the compiler to emit calls to objc_msgSendSuper instead of objc_msgSend.
Since there is no such function as objc_msgSendSuperSuper, what you want can't be done.
You'll have to rely on a method with a different selector instead.
You can replace the method at runtime with your own custom method like so:
#import <objc/runtime.h>
@implementation UIButton(Custom)
// At runtime this method will be called as buttonWithType:
+ (id)customButtonWithType:(UIButtonType)buttonType
{
// ---Add in custom code here---
// This line at runtime does not go into an infinite loop
// because it will call the real method instead of ours.
return [self customButtonWithType:buttonType];
}
// Swaps our custom implementation with the default one
// +load is called when a class is loaded into the system
+ (void) load
{
SEL origSel = @selector(buttonWithType:);
SEL newSel = @selector(customButtonWithType:);
Class buttonClass = [UIButton class];
Method origMethod = class_getInstanceMethod(buttonClass, origSel);
Method newMethod = class_getInstanceMethod(buttonClass, newSel);
method_exchangeImplementations(origMethod, newMethod);
}
Be careful how you use this, remember that it replaces the default implementation for every single UIButton your app uses. Also, it does override +load, so it may not work for classes that already have a +load method and rely on it.
In your case, you may well be better off just subclassing UIButton.
Edit: As Tyler notes below, because you have to use a class level method to make a button this may be the only way to override creation.
Jacob has a good point that category methods act differently than subclass methods. Apple strongly suggests that you only provide category methods that are entirely new, because there are multiple things that can go wrong otherwise - one of those being that defining a category method basically erases all other existing implementations of the same-named method.
Unfortunately for what you're trying to do, UIButton seems to be specifically designed to avoid subclassing. The only sanctioned way to get an instance of a UIButton is through the constructor [UIButton buttonWithType:]. The problem with a subclass like Jacob suggests (like this):
@implementation MyCustomButton
+ (id)buttonWithType:(UIButtonType)buttonType {
return [super buttonWithType:buttonType]; //super here refers to UIButton
}
@end
is that the type returned by [MyCustomButton buttonWithType:] will still be a UIButton, not MyCustomButton. Because Apple hasn't provided any UIButton init methods, there's not really a way for a subclass to instantiate itself and be properly initialized as a UIButton.
If you want some customized behavior, you can create a custom UIView subclass that always contains a button as a subview, so that you can take advantage of some of UIButton's functionality.
Something like this:
@interface MyButton : UIView {}
- (void)buttonTapped;
@end
@implementation MyButton
-(id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = self.bounds;
[button addTarget:self action:@selector(buttonTapped)
forControlEvents:UIControlEventTouchUpInside];
[self addSubview:button];
}
return self;
}
- (void)buttonTapped {
// Respond to a button tap.
}
@end
If you want the button to do different things depending on more complex user interactions, you can make more calls to [UIButton addTarget:action:forControlEvents:] for different control events.
Reference: Apple's UIButton class reference
A more pleasant option (for debugging) is to add a category method which does it for you:
- (void) callSuperMethod { [super overriddenMethod]; }
The fact that there’s no more convenient way to do this is very much by design.
You can send [[[object class] superclass] methodForSelector: ...] and invoke through the IMP pointer directly, if you want... but if you're doing this, there's probably something very wrong with your application's design.
I'll take the risk of stating the obvious: You call the function, if it's defined in the base class it's automatically available in the derived class (unless it's private).
If there is a function with the same signature in the derived class you can disambiguate it by adding the base class's name followed by two colons base_class::foo(...). You should note that unlike Java and C#, C++ does not have a keyword for "the base class" (super or base) since C++ supports multiple inheritance which may lead to ambiguity.
class left {
public:
void foo();
};
class right {
public:
void foo();
};
class bottom : public left, public right {
public:
void foo()
{
//base::foo();// ambiguous
left::foo();
right::foo();
// and when foo() is not called for 'this':
bottom b;
b.left::foo(); // calls b.foo() from 'left'
b.right::foo(); // call b.foo() from 'right'
}
};
Incidentally, you can't derive directly from the same class twice since there will be no way to refer to one of the base classes over the other.
class bottom : public left, public left { // Illegal
};
Given a parent class named Parent and a child class named Child, you can do something like this:
class Parent {
public:
virtual void print(int x);
};
class Child : public Parent {
void print(int x) override;
};
void Parent::print(int x) {
// some default behavior
}
void Child::print(int x) {
// use Parent's print method; implicitly passes 'this' to Parent::print
Parent::print(x);
}
Note that Parent is the class's actual name and not a keyword.
In your particular example, +superclass is actually the way to go:
+ (id)someClassMethod {
return [[[self superclass] superclass] someClassMethod];
}
since it is a class method, hence self refers to the class object where +someClassMethod is being defined.
On the other hand, things get a tad more complicated in instance methods. One solution is to get a pointer to the method implementation in the supersuper (grandparent) class. For instance:
- (id)someInstanceMethod {
Class granny = [[self superclass] superclass];
IMP grannyImp = class_getMethodImplementation(granny, _cmd);
return grannyImp(self, _cmd);
}
Similarly to the class method example, +superclass is sent twice to obtain the supersuperclass. IMP is a pointer to a method, and we obtain an IMP to the method whose name is the same as the current one (-someInstaceMethod) but pointing to the implementation in the supersuperclass, and then call it. Note that you’d need to tweak this in case there are method arguments and return values different from id.
Thanks to Bavarious who inspired me to involve some runtime staff.
Briefly, the desired hypothetical line:
return [super.super alloc];
can be transformed in this "real" one:
return method_getImplementation(class_getClassMethod([[self superclass] superclass], _cmd))([self class], _cmd);
To make it relatively more clear, it can be expanded as follow:
Method grannyMethod = class_getClassMethod([[self superclass] superclass], _cmd);
IMP grannyImp = method_getImplementation(grannyMethod);
return grannyImp([self class], _cmd);
First please note that this code
-(ParentClass *)field:(NSArray *)fields{
ParentClass *pc = [[ParentClass alloc] init];
// code
return pc;
}
Doesn't look right from the software design perspective. From what you posted it seems that ParentClass instances can create and return other instances of its own type from the field method. This doesn't look ok, but it could be fine depending on what your intentions are.
Consider making ParentClass and FieldClass different classes if that makes sense.
Regarding the subclass, the way of doing what you want would be this:
-(ParentClass *)field:(NSArray *)fields
{
// code
return [super field:fields];
}
Note that I changed the returned type to be (ParentClass *), and the self to super. You cannot return a ParentClass object in the place of a SubClass object (the latter could have extra data that former doesn't know about). Doing the opposite is valid (you can return a Subclass object when someone expects to receive an object of ParentClass type).
Having said that is pretty unclear what you're trying to achieve, I'll tell what's wrong. First of all isn't enough to cast a pointer to a base class pointer, to call the superclass method, you should call it this way:
return (Subclass*) [super field:fields]; // Still wrong
But you're break polymorphism, and as the method signature says, you're returning a Subclass object, and the user that calls this method expects to have a Subclass object, but at the first call of a method that is just implemented by the subclass, it crashes because you're returning an instance of the superclass. Maybe is enough for you to change the method signature to return a ParentClass pointer, but this makes the method useless, why overriding it? It isn't pretty clear what you're trying to do, and what's your logic path.
Edit
Having seen the code that you posted on Github, here the situation is pretty different. In the Java code,t he method field returns this, so no new object gets created, and the method is just used for side effects. The add method doesn't break polymorphism, because just the object reference is of the parent class type, but if executed on a subclass it returns the object itself (this), which is of the subclass type.
In Objective-C for these cases the id type is used, which is used to represent a whatever object pointer, to a whatever class. You could also use the ParentClass type, but I'll stick to conventions. Here's an indicative code:
@implementation ParentClass
@synthesize endpoint
- (id) add: (NSString*) endpoint fields: (NSArray*) fields
{
<code>
return self;
}
- (id) field: (NSArray*) fields
{
return [self add: self.endpoint fields: fields];
}
@end
@implementation SubClass
- (id) field: (NSArray*) fields
{
< Additional code >
return [self add: self.endpoint fields: fields];
}
@end
You can, as Objective C method dispatch is all dynamic. Just call it with [self methodOfChild], which will probably generate a compiler warning (which you can silence by casting self to id).
But, for the love of goodness, don't do it. Parents are supposed to provide for their children, not the children for their parents. A parent knowing about a sub-classes new methods is a huge design issue, creating a strong coupling the wrong way up the inheritance chain. If the parent needs it, why isn't it a method on the parent?
Technically you can do it. But I suggest you to alter your design. You can declare a protocol and make your child class adopt that protocol. Then you can have to check whether the child adopts that protocol from the super class and call the method from the super class.
I'm currently playing around with Swift and just realized that when subclassing another class and overriding a method, I don't have to call the same method on super like you would expect. I could have sworn that other languages forced the call to super, but maybe I'm remembering incorrectly? The only methods that seem to force the call to super, at least in Swift, are initializers.
Why is not calling to super allowed when overriding inherited methods? Isn't there a huge danger here in missing out on required parent class implementation details of the method? I assumed that the compiler would immediately complain and force me to call super.
Here's a basic code example:
class AppUser {
var name: String
var username: String
init(name: String, username: String) {
self.name = name
self.username = username
}
// Pretend this is actually very important and not just a print statement :)
func importantFunctionality() {
print("Parent class")
}
}
class SpecialAppUser: AppUser {
var specialId: String
init(name: String, username: String, specialId: String) {
self.specialId = specialId
super.init(name: name, username: username)
self.name = name
self.username = username
}
override func importantFunctionality() {
super.importantFunctionality() // I can comment this out or delete it with no complaints from the compiler
print("Child class")
}
}
let user1 = AppUser(name: "swift", username: "alwaysbeswift")
let user2 = SpecialAppUser(name: "swift", username: "alwaysbeswift", specialId: "12345")
user1.importantFunctionality()
user2.importantFunctionality()You can't even use reflection. Something like
Class superSuperClass = this.getClass().getSuperclass().getSuperclass();
superSuperClass.getMethod("foo").invoke(this);
would lead to an InvocationTargetException, because even if you call the foo-Method on the superSuperClass, it will still use C.foo() when you specify "this" in invoke. This is a consequence from the fact that all Java methods are virtual methods.
It seems you need help from the B class (e.g. by defining a superFoo(){ super.foo(); } method).
That said, it looks like a design problem if you try something like this, so it would be helpful to give us some background: Why you need to do this?
You can't - because it would break encapsulation.
You're able to call your superclass's method because it's assumed that you know what breaks encapsulation in your own class, and avoid that... but you don't know what rules your superclass is enforcing - so you can't just bypass an implementation there.
The usual rule of thumb is that when you are overriding a method that does some kind of initialization, you call super first and then do your stuff. And when you override some kind of teardown method, you call super last:
- (void) setupSomething {
[super setupSomething];
…
}
- (void) tearDownSomething {
…
[super tearDownSomething];
}
The first kind are methods like init…, viewWillAppear, viewDidLoad or setUp. The second are things like dealloc, viewDidUnload, viewWillDisappear or tearDown. This is no hard rule, it just follows from the things the methods do.
Just check the corresponding documentations. For instance, when to call super in overridden methods of UIViewController:
didReceiveMemoryWarning : You can override this method (as needed) to release any additional memory used by your view controller. If you do, be sure to call the super implementation at some point to allow the view controller to release its view. [Means the order is of no importance.]
loadView : Your custom implementation of this method should not call super.
setEditing:animated : This method should invoke super’s implementation before updating its view. [Means the order is of importance.]
viewWillAppear, viewDidAppear, viewWillDisappear, viewDidDisappear: If you override this method, you must call super at some point in your implementation. [Means the order is of no importance.]
dealloc: If you implement this method but are building your application for iOS 2.x, your dealloc method should release each object but should also set the reference to that object to nil before calling super. [Means the order is of importance.]
Did you realize similar rules for super in viewDidLoad and viewDidUnload methods aren't mentioned? Because you don't need to call super in these.