Hacker News new | past | comments | ask | show | jobs | submit login
Objective-C is just, like, a leaky abstraction over C (metaobject.com)
74 points by mpweiher 7 months ago | hide | past | favorite | 33 comments



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 curious: What's been stopping you from actually writing more/some of new code in Objective C rather than C?


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.


I can't imagine trying to seriously use Objective-C on non-Apple platforms; even with all of my love and nostalgia for the language.

(not the OP)


GNUStep has never been popular, but it's out there (Aug 2023):

https://www.mail-archive.com/discuss-gnustep@gnu.org/msg2550...

As you guys know, Algoriddim makes use of GNUstep on Windows. It has just been released in beta: https://www.algoriddim.com/store/djay-windows-beta


You can use ObjFW https://objfw.nil.im/home pretty easily.


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.


> It's crappy syntax over a couple of "objc_*" helper functions.

Exactly! :-)

And that's a good thing!

Even the keyword syntax in general and Smalltalk syntax specifically...no wonder it gets reinvented.

https://blog.metaobject.com/2020/06/the-curious-case-of-swif...


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.

See https://www.sealiesoftware.com/blog/archive/2009/09/01/objc_... and https://www.mulle-kybernetik.com/weblog/2015/mulle_objc_sele...

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?

https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea...

--

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.


I'd say it's an awesome syntax. Much better than Swift for example.


Any ObjC project I go back to, I can step into and generally remember what the hell I was doing.

This isn’t always the case with Swift, unfortunately.

The older I get, the more and more I think ObjC had many things right.


> 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.

I think that it’s also true of Smalltalk.


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:

https://miasap.se/obnc/data-abstraction.html


What’s the best example of using Objective-Cs message system at runtime that could not have been achieved at compile time?

ChatGPT is giving me contrived examples or examples that could have been achieved with the knowledge of classes at compile time.


Portable Distributed Objects in NeXTSTEP.

Send a message to an object that is actually proxied over the network to another server.

https://en.wikipedia.org/wiki/Portable_Distributed_Objects


Microsoft's COM+ was like, hold my beer. Why use a simple syntax when the compiler could quietly insert a bunch of magic and not tell you?


Yeah, then came WinRT, to extend COM even further.

And after 30 years, the tooling still sucks, even though WinDev loves it so much.


Method swizzling.

Whether that's actually a good feature to have in your language is a different conversation entirely.


Objective-C to me is just the pinnacle of language creation. I love plenty of other languages (hello Python) but I just love coding in it so much .


Which is precisely why it's so powerful.


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.


The proper support is still very limited.

You can still do "bool b = 10" or "int i = true". C being C ("everything is an int") this will be coerced to 1.


> 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.


This article assumed you know objective-c.


Which makes sense, given the title.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: