"As expected, the object-oriented version is slower; about 43% slower, primarily due to the extra overhead of message passing. [..] This overhead is approximately 2.5x that of calling a function and far slower than accessing memory directly with a C expression" -- OOP, and Evolutionary Approach, Brad Cox 1986

Note the comparison to memory access. Nowadays, an actual memory access (that doesn't hit the caches) is more than an order of magnitude slower than a message-send. So if you can save a single DRAM access by performing 10 message-sends, you will come out ahead. So yes, the changes in hardware, particularly the relative changes have made a huge difference. For a lot of programs today, the stuff that comes between stalls waiting for DRAM is essentially free.

Also, Apple has continuously optimised objc_msgSend(), it is quite a beast these days.

Things that make Objective-C/Cocoa code slow are multitude, certainly the prevalence of NSDictionary (and CFDictionary) and other forms of keyed accessing, particularly because NSString, while a great implementation for user-facing Unicode strings, is extremely heavyweight for use as a symbol. Then there are call-return based APIs where streaming would be more appropriate (NSJSONSerialization/NSPropertyListSerailization) and the fact that APIs that look like they would be streaming (keyed archiving, swift coding) actually build NSDictionaries underneath). CoreFoundation was also quite the disaster, performance-wise (more than 2x regression compared to pre-CF Foundation), with Foundation itself also a significant regression, partly because it eliminated convenient collections for C types except bytes (NSData).

Integer or float arrays are trivial to add ( https://github.com/mpw/MPWFoundation/blob/master/Collections... ) and 100 - 1000x faster than NSArrays of NSNumbers. The binary property list format is capable of both random access (lazy loading...) and of directly encoding object graphs, but the APIs don't expose those capabilities.

Stepstone Objective-C had stack allocation for objects, this was eliminated by NeXT and not reinstated by Apple. So objects require heap allocation. Etc.

While I also agree that Objective-C was regarded as too slow compared to C++ in particular, that was never really true. Just as a small example, I did image processing in Objective-C, and yes, if you were to apply OO and message-sending on a per-pixel level, you'd be in a world of pain. But even at scan-line granularity, the overhead was already less than 1%, so in the noise. My Objective-C Postscript interpreter is faster than the Adobe interpreter (C-based last I checked) used by Apple at basic language-oriented tasks such as arithmetic/loops.

And of course, you wouldn't really want to use the OO part of Objective-C for operations on low-level C types such as or integers, the syntax is not really inviting compared to C for that sort of stuff. So I always thought that the hybrid nature (coarse-grain objects, connecting C-level implementations) worked really well, for both expressiveness and performance. The XML Parser example in the book shows how you can combine low-level C character processing with messaging-APIs to get both amazing performance (significantly faster than libxml2) and extremely convenient/powerful APIs. IMHO :-)