respondsToSelector Of Super

| /

respondsToSelector is often used to determine whether an instance implements a method for safe calling.

But please don’t call the respondsToSelector method on super.

Sometimes we only want to call a method of the parent class, but we only know that although the parent class implements a certain protocol, it does not necessarily implement the method in the protocol.

[super message] has the following meaning:

When it encounters a method call, the compiler generates a call to one of the functions objc_msgSend, objc_msgSend_stret, objc_msgSendSuper, or objc_msgSendSuper_stret. Messages sent to an object’s superclass (using the super keyword) are sent using objc_msgSendSuper; other messages are sent using objc_msgSend. Methods that have data structures as return values are sent using objc_msgSendSuper_stret and objc_msgSend_stret.

But respondsToSelector is on NSObject, and the internal check logic does not have special processing for super, but only checks the actual calling instance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@protocol ProtocolA <NSObject>
- (void)funA;
@end

@interface A : NSObject<ProtocolA>
@end
@implementation A
@end

@interface B : A
@end
@implementation B
- (void)funA {
if ([super respondsToSelector:@selector(funA)]) { // Bad! always true!
[super funA];
}
}
@end

The correct way is to call the instancesRespondToSelector method on the parent class to check.

1
2
3
4
+ if ([A instancesRespondToSelector:@selector(funA)]) { // Good!
- if ([super respondsToSelector:@selector(funA)]) { // Bad! always true!
[super funA];
}

There may be an idea whether it is possible to use [super class] to replace A?

Unfortunately this is another mistake, the correct way to do it is [self superclass]

The reason is exactly the same as described earlier.

1
2
3
4
5
(lldb) po [super class]
B

(lldb) po [self superclass]
A
1
2
3
4
5
6
+ if ([[self superclass] instancesRespondToSelector:@selector(funA)]) { // Good!
+ if ([A instancesRespondToSelector:@selector(funA)]) { // Good!
- if ([[super class] instancesRespondToSelector:@selector(funA)]) { // Bad!
- if ([super respondsToSelector:@selector(funA)]) { // Bad! always true!
[super funA];
}