开发者

How do warnings on undefined selectors work in Objective C?

开发者 https://www.devze.com 2023-02-04 19:14 出处:网络
Is there any way to make the compiler raise a warning when calling a selector that is not defined ? For example, I have this call somewhere :

Is there any way to make the compiler raise a warning when calling a selector that is not defined ? For example, I have this call somewhere :

methodcall time1:[[self.H1time copy] stringValue]

stringValue method does not exist anymore in the H1time class and the compiler did not raise anything.

copyWithZone is declared as

- (NSHour*)copyWithZone:(NSZone *)zone;

The compiler raises a warning inside NSHour if I call [self stringValue]. but not in methodcall time1:[[self.H1time copy] stringValue]

I saw this miss only because of an intensive test followed with a crash.

I'd like the compiler prevent me of such wrong calls. How may I do this ?

Some tests that says that the compiler may warn in the above case :

NSHour *titi = [NSHour hourWithString:@"00:00:00"];
    id toto = [titi copy];
NSString *str2 = [toto doThis];  --- Wa开发者_如何学运维rning : "No doThis Method found"

Another test :

NSHour *titi = [NSHour hourWithString:@"00:00:00"];
NSString *str3 = [[titi copy] doThis];  --- Warning : "No doThis Method found"


If you want the compiler to warn you, cast the object returned by -copy to the type you’re expecting, e.g.

[(NSHour *)[self.H1time copy] stringValue]

(by the way, I wouldn’t use the NS prefix for my classes since it’s a prefix used by Apple.)

As others have stated, -copy is declared as returning a value of type id. Since id denotes a generic object, the compiler doesn’t have any information as to which selectors the return value can respond to. It does know that there exists a stringValue selector (because a few Cocoa classes declare -stringValue methods), so it accepts that.

But I’ve overridden -copyWithZone: to return NSHour *!

And that’s the right thing to do if you need custom copying behaviour since -copy is just a convenience method that invokes -copyWithZone:. However, as stated above, your code invokes -copy, which hasn’t been overriden to return NSHour *.

What if I also override -copy so that it returns NSHour *?

Then the compiler would know that and would warn you in case you’re trying to send a message containing a selector that has no corresponding method in NSHour.

Should I do this?

Well, for the most part, not really.

Have you ever wondered why all examples of overriding -init return id instead of a class type? This happens because the Objective-C runtime creates a single method description for a given selector. Consequently, methods in different classes having same selectors (i.e., same method names) should share the same argument types and return types.

So, from the runtime perspective, the selector init is described as a method with that name and return type id. Similarly, the selector copy is described as a method with that name and return type id. If you reuse a selector name in methods with different return types or argument types, you’re confusing the runtime system, which could cause a crash or data being mangled.

That being said, when you’re statically typing your objects, you’re giving information to the compiler so that it knows which methods are available, and it will use the appropriate methods regardless of methods in other classes having different return types and argument types.

This thread on cocoabuilder.com has more information on the subject, including a reply by Greg Parker, who works on the Objective-C runtime.


The copyWithZone: method seems to be defined to return id in NSCopying:

- (id) copyWithZone: (NSZone*) zone;

And so is the convenience copy method of NSObject:

- (id) copy;

You don’t get warning for the stringValue message because the receiver is id (= any object) and there is a stringValue message defined somewhere (like on NSNumber). On the other hand there is no doThis defined anywhere, so that the compiler can be reasonably sure that’s probably a typo, regardless of the receiver type. (And if you insist, you can always call performSelector: to get rid of the warning.)

The strange thing is the compiler letting you get away with a copyWithZone: with a different type signature. Maybe your parent already implements NSCopying and your copyWithZone: version is simply skipped because of the different signature?


Edit: this answer assumed copy was returning an object of type id, which isn't the case, so doesn't apply here.


Firstly, it's important to understand that in Objective-C you send messages not call methods. This is a very important distinction, because it means the target of the message (in this case 'stringValue') is resolved at runtime.

When you call:

[self stringValue]

...XCode is clever enough to figure out that 'self' doesn't respond to the 'stringValue' message, because the message receiver is the same object as the message sender.. But when you call:

[[self.H1time copy] stringValue]

...XCode doesn't know what [self.H1time copy] will resolve to until runtime. The system doesn't know in advance what [self.H1time copy] will respond to, so you don't get a compiler warning.

This seems odd, but is by design. It's one of the big differences between Objective-C and C++. So I don't believe (and I'm happy to be proven wrong) that you can have a warning on an unknown selector, because of Obj-C's message centric design.

0

精彩评论

暂无评论...
验证码 换一张
取 消