The inventor of Objective-C, Brad Cox wrote the classic Object-Oriented Programming: An Evolutionary Approach where he defines the concept of "Software-IC" for "Software Component" based systems/design/programming. The underlying C language provides the gate/block-level granular abstractions while the OO features grafted on top provides the chip-level reusability with loose coupling. This is a great mental model to have while programming large systems. The book itself is a classic on how to evolve a language.
Brad Cox's above book and Bertrand Meyer's Object-Oriented Software Construction (using the Eiffel language) were my guides to Object-Oriented Design/Programming and Software Engineering in general and i am eternally grateful to them.
I still program in C at times and would love what Objective-C offers in it's place.
C++? Not so much because, in essence, that is also a leaky abstraction where you have to care about even more issues. Static versus dynamic methods to give an example: code looks the same but works subtly very differently depending on whether the methods used are static or dynamic. So you cannot ignore how the mechanics of the language works underneath and there is a continuous high mental load.
IMO Objective C is simpler - the OO side of it is all dynamic. For performance optimisations you can drop to C and you can really know where you are and what to expect.
To be fair I haven't used Objective-C enough so perhaps there are things about it which would turn me off it in the end. I liked Go for example but the way modules work eventually wore me down.
Just that I was most often working on pre-existing projects like GNU Make. I can't force them to go for Objective-C.
When I'm doing something on my own it's usually so small that it's not really worth it - something like a python C module where there's already a kind of OO model built in. I think my last C program was a set of functions for GNU Make that you can load up - a collection of tiny functions to do simple things that GNU Make should have been able to do 15 years ago but is too constipated to get on with.
Once or twice I've had the perfect sized mini-project but have been in a team where using Objective-C would get me into trouble because the rest of the team love C++ or whatever and would immediately demand that I use that instead. For some reason using C didn't bother them.
It's crappy syntax over a couple of "objc_*" helper functions. One of these functions creates an object, another (objc_msgSend) sends a string called a "selector" and a number of arguments associated with it. The dynamic dispatch happens there – the selector string is mapped to a function address at runtime.
The message send syntax has one important nuance that you will be hard-pressed to somehow simulate with CPP: the selectors are actually interned so the message dispatch does not compare strings, but pointers. This difference has very meaningful performance impact.
The ObJC runtime C API has functions to convert strings to selectors, which you can do once upfront instead of each time objc_msgSend() is called. AFAIK the ObjC compiler can do this step even during compilation so that no method string names are included in the binary, but I'm not sure.
But I think it's no longer quite correct to describe ObjC as plain syntax sugar over the ObjC runtime C API (same as it is no longer correct to say that C++ is simply a preprocessor for C - AFAIK both ObjC and C++ started as such preprocessors), especially when it comes to features like ARC where the compiler does static lifetime tracking on ObjC object references. I don't think this can be replicated in plain C (and in C++ only less efficiently, since smart pointers are a stdlib feature and oblivious to the compiler).
PS: I actually do like the ObjC syntax. One great feature is that ObjC leaves the C syntax and semantics alone, which means that ObjC automatically supports the latest C standard. C++ would be a much better option today if it went the same route, but instead the 'C subset' in C++ is stuck somewhere ca 1995.
> AFAIK the ObjC compiler can do this step even during compilation so that no method string names are included in the binary, but I'm not sure.
That would be possible for static binaries, but I don't see how that would work for dynamic libraries. Two libraries or a library and the executable would need the strings around in order to ensure they both got the same global address. You could mangle the strings to dynamic symbols so that it's just regular dynamic symbol resolution to get multiple loaded entities to agree on an address, but in that case, the selector string is still present in the binary in mangled form.
Dyld (dynamic linker) does this I think, it's effectively similar to other relocations. Each dynamic library brings its own set of selectors (I just think of them as strings in the data section of the binary, referenced indirectly via a pointer in the objc_selrefs section of the binary which can be updated as needed) and at runtime they're uniqueified.
I actually can't find much authoritative discussion about this on the internet, just those two posts. But since the objc-runtime is open source, you can probably find it there. I think it might be this method?
But I'm not sure what GP meant by "at compilation". I suppose the compiler could be smart by reading the selectors of any shared libraries you link against and avoid duplicates in _your_ binary, but that wouldn't work for two shared libraries that know nothing about each other nor anything mapped in via dlopen.
And in fact Brad Cox has stated that it was this at the time crucial optimization that made them go for a full pre-processor instead of just a couple of macros.
Once they had the need for the pre-processor, they decided they might as well make some usable, Smalltalk-derived syntax.
> You don't program your computer with LISP or Smalltalk. Your computer runs a separate LISP or Smalltalk computer that you can then program on its own terms and in its own world.
That’s not really true of all Lisps! Common Lisp may be compiled straight down to assembly (that’s what SBCL does). It can call straight out to the host OS, just like a C program. Now, due to things like garbage collection you have to manage things a bit at the edges, but that’s no different from a language like Go, and I think that no-one would say that Go is its own machine.
It’s true of some Lisps, such as Emacs Lisp. Emacs is really a virtual Lisp machine with an editor as a shell.
The real deal is Obj-C++ - now there is a powerful combo xD
For example, we used template wrappers around NSArray for static type checking and "smart pointers" to avoid manual release/retain calls all over the place, which was glorious.
Where can I read more about Objective Assembler? Can you go lower level than obj_msgSend?
Also bonus points for mentioning the work of Piumarta, whose paper on extensible object models is my all-time favourite and has occupied my mind for years.
I couldn't find info on Objective Assembler either.
> Can you go lower level than obj_msgSend?
In Oberon you have low-level message sending.
See this article that builds in small steps through the various OOP strategies and shows message sending in the last example:
I remember a 2 hours debugging session when I started learning Objective-C.
I don't remember what I did wrong but I somehow tripped over Objective-C's boolean which isn't a proper type but just a macro of an int value like it is now in C. I did probably something stupid like "if (b != TRUE) {}".
What made this so much more infuriating was that if I knew it was just a leaky abstraction over C I wouldn't have made that mistake. But I thought it was on the same level as languages from the Pascal family or Java or any other modern programming language.
> but just a macro of an int value like it is now in C.
That's not the case for the last couple of decades (I think since C99?). The underlying type of bool which is defined in stdbool.h is _Bool, which is a "proper" builtin boolean type. The type wrapping in a header is just there to not break existing code which had its own 'bool' typedef. AFAIK in C23, bool is a regular keyword now though.
> What made this so much more infuriating was that if I knew it was just a leaky abstraction over C I wouldn't have made that mistake. But I thought it was on the same level as languages from the Pascal family or Java or any other modern programming language.
Post-rationalization can always blame the tools instead of one owning its own's mistakes. You can blame tools or documentation that omit crucial surprising information or explicitly lies to you. Everything else is assomption you'd better check, or just humbly accept that they will bite you, by definition, in the most unexpected ways.
Brad Cox's above book and Bertrand Meyer's Object-Oriented Software Construction (using the Eiffel language) were my guides to Object-Oriented Design/Programming and Software Engineering in general and i am eternally grateful to them.