Is this a bug or is there a subtle lesson here?
NSNumber *someNumber = @(1);
[someNumber respondsToSelector:@selector(mutableCopy)]; // returns YES (!)
[someNumber respondsToSelector:@selector(mutableCopyWithZone:)]; // returns NO
Apple LLVM 7.1 (iOS SDK 9.3)
This is because NSObject
itself implements -mutableCopy
(for all objects, even those which do not conform to NSCopying
or NSMutableCopying
) by calling into -mutableCopyWithZone:
(so that things implementing NSMutableCopying
get to just implement -mutableCopyWithZone:
without needing to repeat the implementation for -mutableCopy
).
Everything that inherits from NSObject
responds to -mutableCopy
, but if you actually called it, it would crash because NSNumber
doesn't respond to -mutableCopyWithZone:
.
You can see this with
assert([NSObject instanceMethodForSelector:@selector(mutableCopy)] == [NSNumber instanceMethodForSelector:@selector(mutableCopy)])
bbum's analysis summarizes this pretty well — there are some subtle edge cases when doing these checks dynamically, because you may get answers you don't at all expect.
That's odd. I wouldn't expect the class to implement -mutableCopy
. May be fallout from the tagged pointer implementation (if so, then @(REALLYBIGNUMBERTHATISNEARMAX) would change the behavior).
The subtle lesson is that you can't really use respondsToSelector:
for generic functionality tests. Nor can you use isKindOfClass:
. There are many situations where it would fail (NSArray
vs. NSMutableArray
used to behave very strange-- may still-- when trying to use introspection to determine mutability, for example).
Introspection works well for explicitly declared situations like delegation or data sources. Places where there is an @protocol
declaration that specifically defines some set of @optional
methods.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With