"When I first realized the language offered weak typing -- with the (id) variable -- it seemed to go against everything that C stood for in my mind."
These are good observations, and reflect deliberate choices in the design of Objective C.
The funky message syntax makes clear that a sending a message is not equivalent to calling a function. Conflating the two will eventually lead to pain and confusion.
Declaring a variable as "id" does indeed launch you out of C's type system, into a realm where any object can potentially respond to any message, regardless of its type. This leads to a lot of interesting possibilities and patterns that Cocoa (and iOS) leverage heavily.
Sending a message is indeed equivalent to calling a function, specifically the function objc_msgSend (or one of its variants, depending on return type).
It's also very much like calling a virtual function in C++, only slower.
My day job is working on a mixed C++/Objective-C project (Safari). My statement that ObjC method calls are slow is based on staring at a lot of profiles and poking into the assembly. If you study the page you linked, you can see that even the fast path results in four serialized load instructions:
1) Load class pointer from object
movq (%rdi),%r11
2) Load cache pointer from class
movq 0x10(%r11),%r8
3) Load cache entry (IMP) from cache
movq 0x10(%r8,%rcx),%r11
4) Load function pointer from IMP
movq 0x10(%r11),%r11
Notice that each of these loads reads into a register which is then treated as an address by the next load. This does a great job of totally stalling your pipeline while you wait for memory reads. I've been staring at this same grim pattern in the profiler since the days of PowerPC.
C++ virtual function calls are doubly-indirect in the worst case (read vtable pointer from object, read function pointer from vtable). And because it's emitted at the call site and not hidden in a function, the compiler can often reduce it down to singly indirect or even direct link it as a static call, if it can prove the exact type.
Objective-C is not as bad as some say, but if your code is highly optimized, method call overhead can become a bottleneck.
Comments
"Message (function call) syntax seemed funky."
"When I first realized the language offered weak typing -- with the (id) variable -- it seemed to go against everything that C stood for in my mind."
These are good observations, and reflect deliberate choices in the design of Objective C.
The funky message syntax makes clear that a sending a message is not equivalent to calling a function. Conflating the two will eventually lead to pain and confusion.
Declaring a variable as "id" does indeed launch you out of C's type system, into a realm where any object can potentially respond to any message, regardless of its type. This leads to a lot of interesting possibilities and patterns that Cocoa (and iOS) leverage heavily.
Sending a message is indeed equivalent to calling a function, specifically the function objc_msgSend (or one of its variants, depending on return type).
It's also very much like calling a virtual function in C++, only slower.
In the normal case it's still very fast http://www.friday.com/bbum/2009/12/18/objc_msgsend-tour-part...
My day job is working on a mixed C++/Objective-C project (Safari). My statement that ObjC method calls are slow is based on staring at a lot of profiles and poking into the assembly. If you study the page you linked, you can see that even the fast path results in four serialized load instructions:
1) Load class pointer from object movq (%rdi),%r11
2) Load cache pointer from class movq 0x10(%r11),%r8
3) Load cache entry (IMP) from cache movq 0x10(%r8,%rcx),%r11
4) Load function pointer from IMP movq 0x10(%r11),%r11
Notice that each of these loads reads into a register which is then treated as an address by the next load. This does a great job of totally stalling your pipeline while you wait for memory reads. I've been staring at this same grim pattern in the profiler since the days of PowerPC.
C++ virtual function calls are doubly-indirect in the worst case (read vtable pointer from object, read function pointer from vtable). And because it's emitted at the call site and not hidden in a function, the compiler can often reduce it down to singly indirect or even direct link it as a static call, if it can prove the exact type.
Objective-C is not as bad as some say, but if your code is highly optimized, method call overhead can become a bottleneck.
Nice reply, thank you.