You wouldn't dream of implementing a dynamic language these days designed for performance without basic polymorphic inline caching. But Objective C seems to get by fine without it. Maybe it's not as essential as we think.
That's the key thing - other languages which solve this solve it dynamically.
My recent experience has been that indeed inlining is essential for true "zero-cost abstraction"—say an array of integers with index set/get methods—but that once your abstraction is weighty enough that a method is performing more than a few dozen "interesting" instructions, inlining doesn't win much over how well the modern CPU can already optimize across calls.
EDIT: I should add I'm talking about static inlining. I'm sure your typical alien-technology JIT can identify the exact 800 instruction trace in your inner loop and pull it all together into a perfectly machine-sympathetic sequence that runs 50% faster than anything you could construct statically in the absence of profile-driven feedback.
ObjC has per-class out-of-line caches keyed by the selector (intern'd string).
It's true that there's no hope of inlining across a message send, but ObjC's C and C++ compatibility mitigates a lot of this expense. Apps routinely use C and C++ for perf critical sections.
Also some of Apple's APIs are inline C functions (NSMakeRect, etc) and others are designed to minimize dynamic dispatch. Iterating an NSArray typically requires only one message send (see the NSFastEnumeration protocol).
There's also more exotic techniques like IMP caching.