I'm putting together a list of interview questions for someone interviewing to be a Cocoa developer. I'm a programmer, but I've never done Objective-C. I stumbled upon an interesting question that involves dynamic typing. It should be elementary, I've tried it and compiled it, but I'm still not sure how and why it works.
The question is
What happens at compile time and runtime when you do the following:
NSString *s = [NSNumber numberWithInt:3];
int i = [s intValue];
In the debugger I get
i = (int) 3 s = (开发者_开发技巧__NSFCNumber*) 0x383 (invalid address)
The output of NSLog(@"%d",i)
is 3
, and the output of NSLog(@"%@",s)
is 3
.
Can someone give me an explanation of how all of this is handled by the compiler and the runtime system by also trying to keep in mind that I'm completely new to Objective-C and Cocoa, but not at all new to computer science?
Your s is just a standard C pointer, to which an NSNumber
object (pointer to a) is assigned.
NSNumber
, as well as NSString
, responds to intValue
. That's about it.
s
is a pointer which you've declared will point to an NSString
object. It can point to anything, but ideally it should point to an NSString. The code sample, however, has s
pointing to an NSNumber object.
As long as you only send methods to s
that are methods that NSNumber
responds to everything is fine (except maybe for compiler warnings). If you were to try to send an NSString method to that object pointed to by s
that NSNumber doesn't respond to you would get an exception. If the object has a matching selector signature (ie: @selector(intValue)
) the method will be called.
The declaration of NSString for variable "s" is done merely to assist the compiler in interpreting your intention of variable "s". It allows the compiler to enforce static type checking in the case where you intended to use a variable of a certain type but accidentally assigned a variable of a different type. In ObjC a variable can point to any object while the bracket syntax is a means of sending a "message" to the object. Sending a message instructs the compiler to generate code that looks up the function that implements the message. Also in ObjectiveC messages are referred to as selectors. (The low level details are slightly more involved but at a high level that's how it works.) That's the dynamic part of dynamic typic, which is also referred to as "duck typing". The idea is if it looks like a duck you should be able to make it quack like a duck. In essence any object that follows a certain shape can be assigned to a type that fits the shape.
Consider the following:
Dog *myPuppy = [[Cat alloc] init] autorelease];
[myPuppy walk];
Food *preparedDish = [self prepareMealForPet];
[myPuppy eat: preparedDish];
The compiler would flag you here indicating you intended to spend time with a Dog but are actually dealing with a cat. However it is perfectly valid since a cat is shaped similar to a dog in that they both can walk and eat prepared meals. Duck typing lets you get away with this since there are many cases where you would need to accept types of variables that are not known in advance. Furthermore through introspection you can discover the shape of your object at runtime. Consider in the above code if we wanted to ask our puppy to bark. We would get an exception and the app would crash. However we could use introspection to ask if our object responds to the "bark" message to avoid a crash.
if([myPuppy respondsToSelector:@selector(bark)]) {
[myPuppy bark];
}
精彩评论