4 ms·
First, I want to counter your use of "like" in quotes, as if programmers couldn't have perfectly valid reasons to like these languages.
I like Java for its low
First, I want to counter your use of "like" in quotes, as if programmers couldn't have perfectly valid reasons to like these languages.
I like Java for its low-cost allocations, sane implementation of constructors, and checked exceptions that prevent undeclared control flow behavior. The huge trove of solid-quality libraries and a general community focus on unit testing and documentation (perhaps in no small part driven by Apache) has been quite valuable.
I like C# for its type inference, relational algebra ala LINQ, reactive extensions, adoption of F#, and a general R&D focus on adopting language design from the functional languages.
I like Obj-C for the ease of which I can drop to C and assembly, and the ease-of-implementation inherent in Apple's GUI frameworks. That's about it -- there's a lot to dislike about ObjC.
All that aside, and given my own significant familiarity with the underlying runtimes, I'm trying to understand what you think is so advantageous about Apple's ObjC runtime. It is effectively an extremely simple dynamic dispatch runtime, reliant on the OS memory management combined with reference counting, zero support for any complex runtime state inspection (say, to support breaking reference cycles), and now with some C-derived glue for closures and compiler-emitable calls to support somewhat-automatic reference counting. The class registration system has hardly changed in 20 years, has no support for namespaces, the type encodings available at runtime are complex-to-decode-strings, and the whole thing is, quite honestly, the kind of rickety design you'd expect to emerge from C hackers in the late 80s early 90s, incorporating none of the advances in the field from either Sun/Java or Microsoft/C#.
ObjC predates the work on Strongtalk that led to the modern JVM, it either predates or fails to inherit from the language R&D outside of C, and predates the CLR runtime work done by Microsoft. It's a DINOSAUR.
I don't really understand where you see the runtime advantage here, and I spend nearly all of my time writing ObjC.
The only advantage I see to the ObjC runtime is that it's required to interface with their native libraries.
I have read your discussion of the Objective-C runtime three times to try and understand it. Your criticism seems to boil down to "The ObjC runtime is insufficiently complicated." (For example, you say several times that it lacks features X and Y, for many values of X and Y.)
There is a school of thought that says simplicity is a feature. That C++ is an example of "language gone wrong". That the C hackers in the 80s and 90s had the right idea.
It is not reasonable to compare Java and ObjC because in spite of the marketing material, they have very different performance characteristics. For the most part, garbage collectors are not efficient enough to be comfortably used on 5w computers. If you need a non-GC native-performance language, your choice is C++, C, or ObjC. Clearly you're in the "Features are good, use C++" camp, but the people who are not in that camp are also in pretty good company.
Clearly you're in the "Features are good, use C++" camp
It appears that since you didn't understand my criticism, you decided to substitute your own strawman that you could respond to. Your reference to C++ -- and my assumed preference for it -- is an odd choice, given that I did not actually mention the language.
I think C++ is broken for reasons of poor language design. Simplicity is good in both implementation of a language and the use of it, the balance of that is complex and warrants significant consideration.
I will provide an alternative summary, since I think "The ObjC runtime is insufficiently complicated." is both loaded, and incorrect:
The ObjC runtime is poorly architected, shows its age, and is not sufficiently well designed to permit the implementation of features that would grossly simplify the use of the language.
In support of this I mentioned a number of features that simply are non-implementable on the current runtime, including automatic handling of reference cycles. Your contrary (and unsupported) assertion is that GC is "for the most part" not efficient enough for use on 5W computer. Somebody should tell Microsoft and the MonoTouch guys. Even Android's GC is usable, and it's an fairly primitive mark-and-sweep collector.
Can you expand on "shows its age"? Code entropy doesn't increase as a function of time alone. An ANSI C "Hello, World!" from 1990 doesn't show its age in the context of C today, for example.
Is age an issue in its own right, or is it what informs what you perceive as a poor implementation that precludes the implementation of "modern" features? Also, I wonder if you perceive any possibility that progress might actually occur in the form not of accumulating more features on top of the modern fashionable designs but of starting from an earlier base and pursuing an alternate path of evolution to that of extant runtimes — do you?
For example, is it possible that there is some innovation which could occur on top of C which would not be possible in the context of some modern language and its modern runtime which would make it a more desirable platform than the modern alternatives? As someone who works primarily in C (and is a little tickled by commentary elsewhere on this item about C programmers in the '80s and '90s that suggests everyone moved "up" from C to C++; very Microsoft!) I would say that's certainly the case, because there are already things that make C more appealing than C#, Java, etc. An extension to the type system to do optional, minimalistic reference counting rather than having to use GC in a modern language or the STL's reference-counted containers (or something home-grown) in C++ would be a huge step forward for example. The locking annotations available with Clang and GCC have removed much of the value-add for using a language which does more hand-holding around synchronization.
If it isn't that age precludes the possibility of progress, then what's the problem with age? Code rot is an illusion.
Perhaps a matter of taste, but it seems everything you dislike about the Obj-C runtime are the things I enjoy:
> It is effectively an extremely simple dynamic dispatch runtime
Yup! Just the way I like it. It's practically lisp-like in its minimalism. Just the minimum you'd need to get the job done.
> reliant on the OS memory management
This, actually, is one of my favorite points. Letting the JVM grab a giant allocation at startup is fine if all you're going to do with your machine is run JVM programs (i.e. a server), but it sucks when you've got multiple apps all trying to play nice together. The OS has a memory management system for a reason. I'd much prefer my runtime use the existing system, than re-invent the wheel (as it were).
> with reference counting
Right, well...I did mention that the lack of a GC kinda sucks. I suppose it's a trade-off that one must make if you are going to rely on the OS to manage memory. Still, I can't help but feel that some sort of opt-in system could be made to work at runtime.
> zero support for any complex runtime state inspection (say, to support breaking reference cycles)
Of course, the flip-side to this is that runtime performance is more predictable, since the runtime isn't attempting to do anything overly complex. I can see how you might argue this either way: more runtime introspection eases the burden on language implementors, but it also is a sunk cost whether you want it or not.
> The class registration system has hardly changed in 20 years
"It seems that perfection is reached not when there is nothing left to add, but when there is nothing left to take away"
> has no support for namespaces
I'm not completely convinced this is a problem domain that I want my runtime solving for me. The addition of two-level namespacing to Mach-O addresses the only time I can think that I definitely want runtime managed namespacing. If I want namespacing to be part of my object model, let me determine how it should work. (It's always mildly annoyed me that Java's reverse-DNS-style bleeds into almost every JVM-based language.)
> the type encodings available at runtime are complex-to-decode-strings
The type encodings are simple C strings!
> the whole thing is, quite honestly, the kind of rickety design you'd expect to emerge from C hackers in the late 80s early 90s
If by that you mean that the central message dispatch routine is hand-optimized assembly for each platform the runtime is available on... I'll have some more of that, please!
Edit: Oh, and about the "like" part...I was being mildly facetious. There are aspects of all 3 languages that I enjoy, but I doubt they'd have become as popular as they have on their merits alone. (Well, ok, given the landscape at the time, Java's popularity could be said to be fairly won...)
Not sure what you mean with "zero support for runtime state inspection", can you elaborate? Objective-C supports inspection of instance variables and messages (name + send/return types), so once you are dealing with live objects you can find out pretty much anything you want.
There's even some support for rudimentary stack inspection, but that's more limited.
Yes, it's a dinosaur, but a mighty cool one :-)
Specifically "complex runtime state inspection (say, to support breaking reference cycles)".
There's no safe way to walk the object graph to break reference cycles, despite Apple implementing both ivar indirection (to solve the fragile base class problem), and implementing what should be self-describable block structures.
Information such as whether a particular captured variable/ivar is a weak or strong reference would have been very simple and cheap to include in the runtime metadata, but was -- like many other things -- not included.
In another comment in this thread I described the difference between Apple's runtime/language design and everyone else's:
This sort of hackish minimalism is the polar opposite of the careful considered and long-term focused design that Microsoft (and even Sun) applied to the core of their platform. Compare the completeness (and longevity) of the JVM Virtual Machine specification to the ObjC runtime. Over roughly the same amount of time, Apple has had to introduce complete ABI-breaking fundamental changes of to their runtime to implement extremely basic language enhancements. In contract, the JVM specification has not, to my recollection, been broken once over the course of 20 years. Additions have always been forward-compatible.
The weakIvarLayout field seems to do exactly what you're saying for ivars, though there is no equivalent for blocks. (Whether you can [safely] do it anyway depends, I suppose, on whether you're willing to inspect machine code; it's not that hard, though the suggestion does nothing to disprove your claim of hackish minimalism.)
Is your criticism the same for the newer 64-bit runtime? https://developer.apple.com/library/mac/#releasenotes/Cocoa/RN-ObjectiveC/_index.html https://developer.apple.com/library/mac/#releasenotes/Cocoa/...
> The ObjC runtime is poorly architected, shows its age, and is not sufficiently well designed to permit the implementation of features that would grossly simplify the use of the language.
The first two points are not defined in a sufficient way that would allow us to have a meaningful conversation about them. What does it mean to be poorly-designed? What does it mean to be old, and why is old bad? We cannot make progress here without semantic introspection. On the third topic, however:
> In support of this I mentioned a number of features that simply are non-implementable on the current runtime, including automatic handling of reference cycles
I'd like to examine this particular assertion in substantial detail, because it is apparent to me I have not communicated clearly:
1) First of all, the statement "automatic handling of reference cycles is not possible to implement because of the Objective-C runtime" is false. It is false because it has in fact been implemented, in spite of the Objective-C runtime: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/GarbageCollection/Articles/gcUsing.html#//apple_ref/doc/uid/TP40008006-SW2 https://developer.apple.com/library/mac/#documentation/Cocoa...
2) I've made the general argument that many features are not useful on a 5w computer. Now I will make the specific argument that detecting retain cycles is not useful on a 5w computer:
> We have no heap scans, no whole-app pauses, and no non-deterministic releases. We really think that a smooth, fluid interface is important to our customers, and that great battery life is also important. And with that, GC is deprecated. - Apple, WWDC 2012 session 406, about 34 minutes in
Now extending this two-case line of reasoning to the general list of features: I do not believe (and you have provided no evidence to support) that the Objective-C runtime is the limiting factor for any feature you have listed. I also do not believe (and you have provided no evidence to support) that any feature is useful on a 5w computer. In this particular most-favored case, both of these problems apply, but I believe at least one problem applies to every feature you have listed.
> Somebody should tell Microsoft and the MonoTouch guys. Even Android's GC is usable, and it's an fairly primitive mark-and-sweep collector.
I think they already know:
> The occurrence of this is non-deterministic, and may happen at inopportune times (e.g. in the middle of graphics rendering). If you see this message, you may want to perform an explicit collection elsewhere, or you may want to try to reduce the lifetime of peer objects. http://docs.xamarin.com/android/advanced_topics/garbage_collection http://docs.xamarin.com/android/advanced_topics/garbage_coll...
As I understand it, MonoTouch's optional SGen does a better job, but still uses a stop-the-world algorithm for "large" objects. Given the memory constraints of most iOS devices, lots of things are large relative to the amount of memory you have available. For example, my real-world iPad currently has 90MB in the free pool, which means that a single layer that draws to screen is 10% of the immediately available memory in pure graphics buffer allocations. (It is possible to increase the available pool, and in fact iOS will instruct apps to dump what they can if more is requested, but this is slow and also not being a good memory citizen).
The ObjC runtime is poorly designed in part due to the fact that it fails to cleanly and adequately represent the necsessary metadata to support new features, and thus requires repeated ABI breakage as Apple stumbles about trying to implement enhancements. As compared to other runtime designs, and as I've noted elsewhere in comparison to the JVM specification, this is a uniquely poor showing for a runtime.
To address your claim that the runtime allows for GC because GC was implemented poorly for Mac OS: The previous GC implementation had fatal flaws in performance and execution, required substantial runtime changes, and involved breaking the previous ABI and requiring binaries to declare conformance to a new one.
ARC required a lesser amount of runtime changes, but also failed to include basic bookkeeping to support handling of cycles in the future.
As for accepting Apple's assertions about GC at face value -- this is the same organization that proudly trumpeted their poor implementation of GC at WWDCs past, and migrated their entire set of Mac OS frameworks to support it. Apple makes serious mistakes and are often proven wrong.
GC at Apple died because of a poor implementation, political fallout from it, and lack of adequate investment in it. I find it disturbing and sad that all Apple has to do is state 'GC bad' (for internal Apple political reasons!), and suddenly the masses of Apple developers with no significant experience using GC -- much less implementing it -- are parroting the line.
You reference that SGen requires hinting; the fact is, it does work, and the required hinting is nothing compared to dealing with reference cycles in block heavy code. Regardless of that, Apple should be capable of bringing more computer science firepower to this problem than Mono is. Someone else in this thread has already linked to research into hybrid refcount GCs.
The way this argument is going is a very eloquent "he said, he said". I'd like to push it into a better discussion. Could you drop the rhetoric about "parroting the WWDC line" and "Apple stumbling about", the ad-hominem characterizations of developers who haven't implemented a GC, etc.? I'm going to require you to provide hard facts or examples to support your claims, and not present conclusions as arguments, and not muddy the waters.
> The ObjC runtime is poorly designed in part due to the fact that it fails to cleanly and adequately represent the necsessary metadata to support new features, and thus requires repeated ABI breakage as Apple stumbles about trying to implement enhancements.
The ABI was "changed" once. When I say it was "changed", I really mean that the new ABI was released for the first time on new architectures that did not previously have an ABI at all, and the old architectures' ABI did not change. Assuming that your objection to ABI "breakage" was compatibility-based in nature, this objection would be unfounded.
You have also failed to make a claim that any particular feature is both useful and unimplementable, and also failed to support such a claim. Please make and support claims, not conclusions.
> As for accepting Apple's assertions about GC at face value -- this is the same organization that proudly trumpeted their poor implementation of GC at WWDCs past, and migrated their entire set of Mac OS frameworks to support it.
To be fair, garbage collection was released before iOS was released. Apple's position is today, and has always been, that garbage collection is infeasible on iOS. What has changed between 2006 and 2012 is that it now makes a lot of sense from both a developer mindshare point of view and an Apple engineer cost/benefit point of view to make Mac OS be as close to iOS as possible, because that's where the money is. There are very good reasons why (e.g. non-deterministic finalization) it is impossible to share code and illogical to share engineers between ARC and GC codebases.
> GC at Apple died because of a poor implementation, political fallout from it, and lack of adequate investment in it.
This contradiction is baseless (e.g. unsupported), so on that basis I am not compelled to respond. It also contradicts my discussions with engineers who were involved in the decision, so if you were to provide a basis, I have first-hand accounts with which to refute you.
> Regardless of that, Apple should be capable of bringing more computer science firepower to this problem than Mono is.
And here is the fundamental disconnect: you believe that currently-known GC algorithms are "good enough" for general use on iOS. I do not. Apple does not.
Thinking it through logically, there are three possible root causes for this disagreement:
1. You and I could have a different conception of the hardware capabilities of iOS devices
2. You and I could have a different conception of the performance characteristics for currently-known GC algorithms
3. You and I could have a different threshold for user pain, so you might characterize a certain performance penalty as "acceptable" that I would characterize as "unacceptable."
We've danced a little bit around #1 and #2, and we can talk some more about those. But I think the real issue might be #3.
For example, the native screen refresh rate is 60fps. I would consider it unacceptable to skip five consecutive frames, and unacceptable to skip more than 15 frames as a sustained average over many seconds. Mono documents "major collections" to take a second, which fails both criteria. [1]
I would also consider it unacceptable that, upon returning to the run loop (or equivalent common synchronization point), more than 15% of garbage bytes remain uncollected. There's just not that much memory to go around.
At this point, I have laid out in very specific detail what I mean by "performs acceptably". Now, you can either A) tell me that my demands are insane, in which case we simply have a very different idea of what "responsive user interface" means, and there's really nothing more to be said to convince the other. Or, B) demonstrate that there exists some GC implementation which fulfills these requirements, and additionally demonstrate that it cannot be implemented with the current ObjC runtime.
I think this is the the healthiest way forward for the discussion, because it leaves open the possibility that we may learn something. If instead we return to the things I listed in the first paragraph, I think we both have better uses of our time.
[1] http://android.xamarin.com/index.php?title=Documentation/GC http://android.xamarin.com/index.php?title=Documentation/GC
> This, actually, is one of my favorite points. Letting the JVM grab a giant allocation at startup is fine if all you're going to do with your machine is run JVM programs (i.e. a server), but it sucks when you've got multiple apps all trying to play nice together. The OS has a memory management system for a reason. I'd much prefer my runtime use the existing system, than re-invent the wheel (as it were).
The OS memory management system is as general-purpose as possible. At the kernel/process interaction level, the APIs are thin abstractions on the underlying VM page mapping system. Below that, at the malloc level, malloc is designed to be as general purpose allocator as possible, without introducing complexity unsupportable in C, such as migrating allocations across generations.
The JVM's lack of ability to rededicate pages to the OS is a failing of Sun's particular implementation, and is not strictly inherent in the design of generational collectors.
Your argument dismantles Sun's architectural choice when used on mobile hardware, but does not address the relative merits of alternative allocation/collection schemes.
> In regards to the class registration system: "It seems that perfection is reached not when there is nothing left to add, but when there is nothing left to take away"
That response has little substance. It's not perfect, or even great, it's merely functional. I would be enthralled if Mach-O two-level namespacing worked with ObjC classes, but it can not.
> The type encodings are simple C strings!
They're C strings, not particularly simple. Try decoding (with lackluster documentation) a complex structure return value. Modern runtimes use much more cleanly structured, cross-referenced data here.
> If by that you mean that the central message dispatch routine is hand-optimized assembly for each platform the runtime is available on... I'll have some more of that, please!
No, that's not what I mean. You'd find just as much complex hand-optimized assembly code in other runtimes, likely more.
I'm referring to design choices such as the use of unnecessarily complex data structures with minimal if not outright missing API to access them, poorly abstracted implementation details (such as exposing C string type signatures as the highest-level representation of a type encoding), minimal/missing/poorly-defined metadata.
An example is the failure to encode type data in blockrefs in the first public release of blocks for ObjC, which made it impossible to implement imp_implementationWithBlock(), due to the need to differentiation between stret vs non-stret return types and select the appropriate trampoline. This required changes to both the compiler and the runtime, and meant that prior to those changes' introduction by Apple, it would have been impossible for an external entity to implement similar functionality.
This sort of hackish minimalism is the polar opposite of the careful considered and long-term focused design that Microsoft (and even Sun) applied to the core of their platform. Compare the completeness (and longevity) of the JVM Virtual Machine specification to the ObjC runtime. Over roughly the same amount of time, Apple has had to introduce complete ABI-breaking fundamental changes of to their runtime to implement extremely basic language enhancements. In contract, the JVM specification has not, to my recollection, been broken once over the course of 20 years. Additions have always been forward-compatible.
An example of the above would be the addition of non-fragile base classes through the use of minimal ivar access indirection. This required a wholesale breakage of the entire language, and thus could only be introduced on 64-bit Mac OS X and on iOS.
Apple's language and runtime design is hackish at best.
Unfortunately, as I recall, the meaning of weakIvarLayout is different depending on whether the code is compiled for ARC, non-ARC, or non-ARC GC/GC-optional. Unfortunately I don't remember the specifics off-hand.
Also, yes, runtime machine code inspection is a ... possibility ... :) It's not that 'hard', but it's certainly harder than clean metadata, and it means maintaining individual introspection code for all supported architectures.