The second thing that I found astonishing was that these strings were actually being used by the runtime to determine what to call, i.e. something that in basically every other native language I've seen would be a direct or indirect call to an address or vtable offset. All this in an application that has no exported functions. I can definitely see how it would make RE extremely straightforward! Efficiency, on the other hand...
"Advancements in the Objective-C runtime"
They need to interoperate with the rest of the platform.
https://www.mikeash.com/pyblog/objc_msgsends-new-prototype.h...
Fortunately it doesn't do a full string comparison - it just compares the value of the string pointers, I understand.
This is not strictly true: On macOS/iOS the OS/frameworks/plug-ins may call your methods, likewise with support for services, input managers, distributed objects, etc.
The responder chain is a good example of this dynamism: The input manager (responsible for interpreting key strokes) translate the user’s input into a message, e.g. “copy” or “insert A” and then finds the first object in the chain of objects that responds to this message.
This chain of objects may consist of standard framework objects (like a text view) or it may be your custom objects (like a view controller subclass).
In most other environments, you would have to create special interfaces for stuff you want to make public, this means only that stuff suffers the performance overhead, but you generally pay the cost in code complexity, see e.g. Window’s Component Object Model.