检查子类是否覆盖了一个方法
Check if subclass overrides a method
是否可以检查一个子类是否实现了一个方法,该方法存在于其直接超类或其超类的某些超类中,等等?
例如我将 UIView
子类化,为我的应用创建自定义通用视图,然后将此自定义视图子类化。现在我的一些子类覆盖了 UIView
中的一些方法,有些则没有。我只想调用此方法,如果它实际上被覆盖,我不想调用默认的 UIView
方法。
有没有办法检查这个,即使用类似于 respondsToSelector:
的方法?
编辑:
这个问题与 Objective-C detect if class overrides inherited method 中提出的问题不同,因为我真的不知道或者不想关心哪个超类最初实现了该方法。
不幸的是,没有 respondsToSelector:
的等价物可以为您完成这项工作。正如您可能知道的那样,respondsToSelector:
的工作方式与 return YES
相同,只要 class 本身或其任何 superclasses 实现这个方法。
但是,为什么不直接将该方法的空实现放入您的自定义子程序中class,这样您就可以确保调用它不会产生任何效果,并且不会在超级class。你有没有想过这个?
更新:
虽然没有等同于 respondsToSelector:
的方法,但您可能需要查看 Objective-C 运行时 。它是一个库,允许您在 运行时检查 class 的特征(有点类似于 Java 反射)。查看参考 here。
不是最佳解决方案,但您可以为 UIView subclass 保留一个布尔值成员(我们称它为 UIViewSub
,指示 class 是否实现了所需的行为并检查使用该方法之前的那个布尔值。
你的子classes(那些继承UIViewSub
的)将set/unset这个成员在其构造中相应地(即它是否确实通过实现覆盖它)
基于 Mert Buran 的 answer。
您可以创建一个简单的方法来检查给定的对象是否覆盖了给定的选择器(方法):
-(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;
}
可以这样调用:
[self checkIfObject:someObject overridesSelector:@selector(someSelector)];
使用runtime,你要使用的功能
方法
* class_copyMethodList ( Class cls, unsigned int *outCount );
这将为您提供一个列表供您使用。
扩展 ZeMoon 的回答,我发现没有足够的逻辑来满足所有情况。我需要添加一个检查以确保循环中下一个超类的实例完全实现了指定的选择器。 UIView
的子类就是一个例子。
while (objSuperClass != Nil) {
isMethodOverridden = ([objSuperClass instancesRespondToSelector:selector]) &&
([object methodForSelector:selector] !=
[objSuperClass instanceMethodForSelector:selector]);
if (isMethodOverridden) {
}
}
您可能会问 Why?! ♂️
… 但是如果有一天晚上您发现自己在 Swift 4 中寻找如何做到这一点 – 这就是方法:
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({ [=10=] != 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
适用于 Swift 4.0(并添加了一个可选的 baseClass BELOW,算法会查找覆盖):
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
}
}
是否可以检查一个子类是否实现了一个方法,该方法存在于其直接超类或其超类的某些超类中,等等?
例如我将 UIView
子类化,为我的应用创建自定义通用视图,然后将此自定义视图子类化。现在我的一些子类覆盖了 UIView
中的一些方法,有些则没有。我只想调用此方法,如果它实际上被覆盖,我不想调用默认的 UIView
方法。
有没有办法检查这个,即使用类似于 respondsToSelector:
的方法?
编辑: 这个问题与 Objective-C detect if class overrides inherited method 中提出的问题不同,因为我真的不知道或者不想关心哪个超类最初实现了该方法。
不幸的是,没有 respondsToSelector:
的等价物可以为您完成这项工作。正如您可能知道的那样,respondsToSelector:
的工作方式与 return YES
相同,只要 class 本身或其任何 superclasses 实现这个方法。
但是,为什么不直接将该方法的空实现放入您的自定义子程序中class,这样您就可以确保调用它不会产生任何效果,并且不会在超级class。你有没有想过这个?
更新:
虽然没有等同于 respondsToSelector:
的方法,但您可能需要查看 Objective-C 运行时 。它是一个库,允许您在 运行时检查 class 的特征(有点类似于 Java 反射)。查看参考 here。
不是最佳解决方案,但您可以为 UIView subclass 保留一个布尔值成员(我们称它为 UIViewSub
,指示 class 是否实现了所需的行为并检查使用该方法之前的那个布尔值。
你的子classes(那些继承UIViewSub
的)将set/unset这个成员在其构造中相应地(即它是否确实通过实现覆盖它)
基于 Mert Buran 的 answer。
您可以创建一个简单的方法来检查给定的对象是否覆盖了给定的选择器(方法):
-(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;
}
可以这样调用:
[self checkIfObject:someObject overridesSelector:@selector(someSelector)];
使用runtime,你要使用的功能 方法
* class_copyMethodList ( Class cls, unsigned int *outCount );
这将为您提供一个列表供您使用。
扩展 ZeMoon 的回答,我发现没有足够的逻辑来满足所有情况。我需要添加一个检查以确保循环中下一个超类的实例完全实现了指定的选择器。 UIView
的子类就是一个例子。
while (objSuperClass != Nil) {
isMethodOverridden = ([objSuperClass instancesRespondToSelector:selector]) &&
([object methodForSelector:selector] !=
[objSuperClass instanceMethodForSelector:selector]);
if (isMethodOverridden) {
}
}
您可能会问 Why?! ♂️
… 但是如果有一天晚上您发现自己在 Swift 4 中寻找如何做到这一点 – 这就是方法:
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({ [=10=] != 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
适用于 Swift 4.0(并添加了一个可选的 baseClass BELOW,算法会查找覆盖):
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
}
}