Reputation: 4228
Is it possible to check whether a subclass implements a method that exists either in its immediate superclass or in some superclass of its superclass, etc?
E.g. I subclass UIView
, to make my custom generic view for my app, and then I subclass this custom view. Now some of my subclasses override some method from UIView
and some don't. I only want to call this method if it is actually overridden, I do not want the default UIView
method being called.
Is there a way to check this, i.e. with a method similar to respondsToSelector:
?
Edit: This question is different from the one asked in Objective-C detect if class overrides inherited method because I do not really know or do not want to care which superclass originally implements the method.
Upvotes: 7
Views: 3356
Reputation: 3938
Adapted for Swift 4.0 (and added an optional baseClass BELOW which the algorithm looks for overrides):
extension NSObject {
func checkIf(overrides selector: Selector, baseClass: AnyClass? = nil) -> Bool {
guard var objSuperClass = self.superclass else { return false }
var isOverridden = false
while objSuperClass != nil {
isOverridden = object.method(for: selector) != objSuperClass?.instanceMethod(for: selector)
if isOverridden {
break
}
objSuperClass = objSuperClass?.superclass()
if objSuperClass == baseClass {
break
}
}
return isOverridden
}
}
Upvotes: 0
Reputation: 9085
You might ask Why?! š¤¦āāļø
ā¦ but if one night you find yourself looking how to do this in Swift 4 ā this is how:
import Foundation
extension NSObject
{
func overrides(_ selector: Selector) -> Bool {
var currentClass: AnyClass = type(of: self)
let method: Method? = class_getInstanceMethod(currentClass, selector)
while let superClass: AnyClass = class_getSuperclass(currentClass) {
// Make sure we only check against non-nil returned instance methods.
if class_getInstanceMethod(superClass, selector).map({ $0 != method}) ?? false { return true }
currentClass = superClass
}
return false
}
}
class Foo: NSObject { @objc func mtd() {} }
class Bar: Foo {}
class Baz: Bar { override func mtd() {} }
class Qux: Bar {}
class Fen: Baz {}
Foo().overrides(#selector(Foo.mtd)) // false
Bar().overrides(#selector(Bar.mtd)) // false
Baz().overrides(#selector(Baz.mtd)) // true
Qux().overrides(#selector(Qux.mtd)) // false
Fen().overrides(#selector(Fen.mtd)) // true
Upvotes: 1
Reputation: 12782
Using the runtime, you want to use the function Method
* class_copyMethodList ( Class cls, unsigned int *outCount );
This will give you a list to work with.
Upvotes: 0
Reputation: 3171
Expanding on ZeMoon's answer, I found that there wasn't enough logic to satisfy all cases. I needed to add a check to ensure that instances of the next superclass in the loop implement the specified selector at all. Subclasses of UIView
are one example.
while (objSuperClass != Nil) {
isMethodOverridden = ([objSuperClass instancesRespondToSelector:selector]) &&
([object methodForSelector:selector] !=
[objSuperClass instanceMethodForSelector:selector]);
if (isMethodOverridden) {
}
}
Upvotes: 1
Reputation: 20284
Based on this answer by Mert Buran.
You can create a simple method to check whether a given object overrides a given selector (method):
-(BOOL)checkIfObject:(id)object overridesSelector:(SEL)selector {
Class objSuperClass = [object superclass];
BOOL isMethodOverridden = NO;
while (objSuperClass != Nil) {
isMethodOverridden = [object methodForSelector: selector] !=
[objSuperClass instanceMethodForSelector: selector];
if (isMethodOverridden) {
break;
}
objSuperClass = [objSuperClass superclass];
}
return isMethodOverridden;
}
This can be called as follows:
[self checkIfObject:someObject overridesSelector:@selector(someSelector)];
Upvotes: 10
Reputation: 13713
Not the optimal solution but you can keep a boolean member to your UIView subclass (let's call it UIViewSub
indicating if the desired behavior is implemented by the class and check that boolean before using the method.
Your subclasses (those inheriting UIViewSub
) will set/unset this member accordingly in its construction (i.e if it does override it with implementation or not)
Upvotes: 1
Reputation: 22751
Unfortunately, there is no equivalent to respondsToSelector:
that will do precisely this job for you. As you will probably know, respondsToSelector:
works in the way that it will return YES
as long as the class itself or any of its superclasses implements this method.
But why not just put an empty implementation of the method into your custom subclass, that way you make sure that calling it doesn't have any effect and doesn't call the same method in the superclass. Did you think about this?
Update:
While there is no method equivalent to respondsToSelector:
, you might want to take a look at the Objective-C Runtime. It's a library that allows you to inspect characteristics of a class during runtime (a bit similar to Java reflections). Check out the reference here.
Upvotes: 2