On 10 Aug 2010, at 13:39, Frank Heckenbach wrote:
Jonas Maebe wrote:
You can define as many root classes as you want, with the only restriction that they have to conform to the NSObject protocol (a protocol is similar to an interface in Java/Delphi-style Pascal -- and yes, protocols and classes can have the same name).
Then I probably got confused by that. But still trying to understand the differences coming from C++ one could say, protocols (like Java/Delphi interfaces) correspond to abstract classes with only abstact virtual methods, implementing interfaces corresponds to (multiple) inheritance, so the NSObject protocol would be the common ancestor,
Correct, I think.
all classes implement (inherit from) it, and "id" would then be a pointer/reference to "NSObject_protocol".
First some terminology: * message =~ method * selector (or message selector) =~ message name (in Objective-C, conceptually all dispatching happens based on the message's selector rather than based on vtable indices; in practice, several optimizations are used to minimise the overhead)
Anyway, id is more than just a reference to NSObject_protocol, since you can (without any type casts) send any message to "id". Following the C tradition, that message's selector doesn't even have to known to the compiler (although, just like with most C compilers, you'll get a warning in case the selector isn't declared in the current scope).
E.g., in any Objective-C program, you can write something like this:
id obj, res; ...
res = [ obj nonExistantMessageWithIntPara: 1 andPara: 4.0 ];
This gets translated by the compiler into an equivalent of:
type tempmsgtyp = function(obj: pointer; sel: pselector; arg1: cint; arg2: double): id; cdecl; ... res := tempmsgtyp(objc_msgSend)(obj, sel_getUid('nonExistantMessageWithIntPara:andPara:'), 1, 4.0);
You can also declare a variable as pointing to something conforming to the NSObject protocol, but that would be "id<NSObject> obj", and in that case the compiler only allows sending messages from the NSObject protocol to obj.
In case you are interested in something that cannot be expressed by C+ +'s object model, then have a look at Objective-C categories. A category extends a class without inheritance. It can only declare messages, and for all messages declared in that category the following holds: a) if they don't exist in the extended class, then they are added to the extended class as if they appeared in its "real" declaration (i.e., they are also automatically inherited by all descendants; all messages in Objective-C behave functionally equivalent to "virtual" methods) b) if they do exist in the class, then the category's message *replaces* the original message definition in the class (so if a derived class calls the inherited selector, it will end up in the category's version of the message; and calling the inherited selector from the category version will end you up in the message of the parent class of the extended class: the replaced message itself is gone).
If multiple category's add/replace the same message in the same class, then the result is undefined (which message "wins" depends on the order in which the category's are loaded by the Objective-C runtime). Using this particular functionality of categories is also strongly discouraged.
Category's obviously allow for really ugly code and really cool hacks, but in the Cocoa framework they are mostly used to simply decompose class definitions in functionally independent parts (so no replacing of messages). The advantages are a) they reduces the fragile base class problem (you can add messages even to base classes defined in the system frameworks, e.g., some string processing method specific to your application can be added to the base NSString class) b) smaller header files in case you don't need all functionality (speeding up parsing because of the C model of including headers)
To avoid name clashes, it is recommended to begin all of the selectors with some company/person-specific prefix. It can obviously lead to ugly situations, but in practice seems to work quite well.
And if the object that the id-variable references is not of the other class type? Do you get a runtime exception, or does the assignment still work?
The assignment works, no check occurs (although I guess llvm's Clang may warn if it can statically determine that the assignment is unsafe).
The latter would seem wrong to me, the former corresponds to C++ dynamic_cast.
There are runtime functions you can call to determine inheritance compatibility, so in principle you could implement such a cast expression yourself via the preprocessor (or the compiler could insert checks). But it's not part of the language and indeed dangerous, and I believe that this causes a lot more problems than the category functionality.
Jonas