I should have probably anticipated that people would raise eyebrows and spent more time explaining the point in the LLVM commit, so here it is...
Beside the obvious cost of the h-lookup, it comes with 4 other kinds of costs:
- codegen size
- optimization barrier
- static metadate
- runtime metadata
In addition to `self`, `_cmd` is passed to objc_msgSend to be able to lookup your IMP. A selector is loaded from a GOT-like slot, called a selref, which in arm64 generates assembly akin to:
adrp x1, mySelector@PAGE
ldr x1, [x1, mySelector@PAGEOFF]
Swizzling is a powerful tool, but it requires huge guarantees from the compiler. People will call out "inlining" as the lost optimization, but it's way worse. Even without inlining, the optimizer cannot be allowed to know that a trivial readonly...
An Objective-C method comes with extensive metadata (costs as of today, may change in a future runtime):
- a Method that is an entry in your class method list (24 bytes, 3 relocations)
- a selector (average selector length is in the high 20ies bytes)
- a selref to articulate around it (8 bytes, 1 "relocation")
Properties are more costly as they have an entry in the property list, raising the toll even further
To perform a message dispatch, the runtime build IMP caches. This is not a small cost, and adds up quickly. The VAST majority of IMP caches entries aren't called often and are dead weight of dirty memory that is useless to the process.
While it has always been possible to use C (and the compiler helps you by giving you visibility into your private ivars if you place the C function within your @implementation block), it is cumbersome and irregular in syntax.
Yes, obviously, for the reasons explained above, writing C functions is cumbersome, and rarely used, and an attribute makes it extremely cheap (in keystrokes) to do en masse.