On #4: personally, I believe it is a mistake to attempt to abstract memory management from developers unless you can abstract all resource management (as there is no fundamental difference between memory mappings, file handles, database connections, or minimum-wage bicycle messengers), and in the attempt to fully abstract memory management (as in, with garbage collection) it usually becomes impossible to abstract arbitrary resource allocation (due to non-deterministic finalization; interestingly, this is a similar tradeoff to CAP).
With this realization, Objective-C is actually really good at this, requiring many fewer (if not actually no) try/catch blocks to get "safe" code than is required on seemingly almost every line of Java (which doesn't even have C#'s "using" to help it out, a primitive that causes its own problems due to lack of contracts), and has conventions that make it quite simple to "locally" (as in, without having to read anything not directly around the area you are analyzing) determine whether code you are looking at is handling things correctly.
Therefore, and I kid you not: when I saw "a defense of Objective-C" I was anticipating an article that was going to start with how awesome the memory management was (listing the auto-release pool paradigm as something that people typically haven't developed into mature C++ memory paradigms), followed by a discussion of the balanced tradeoff between fully dynamic typing and dispatch with low-level performance-oriented code; instead, I read "You’ll have to prise my garbage collecter out of my cold, dead hands!", sigh, and go back to coding. :(
>as there is no fundamental difference between memory >mappings, file handles, database connections,
>or minimum-wage bicycle messengers
The number of programs you can write without file handles, database connections or minimum-wage bicycle messengers is dwarfed by the number of programs that are extremely painful to write without dynamic memory allocation. We can argue all day about how 'fundamental' the difference is but there is a profound practical difference well-recognized in the decades of work that's gone into memory GC. 'It's ok/actually awesome that Obj-C kind of blows at memory management because other languages kind of blow at universal resource management' is a pretty specious argument.
I would love to see a reference for the "profound practical difference well-recognized in the decades of work that's gone into memory GC".
Many languages, including Objective-C (although, arguably/mostly Foundation), manage to provide primitives that make it easy to manage either at once; the only high-level complexity you have to give up is cycle detection, which is a serious problem and "known tradeoff" in many fields, including deadlock detection (hence, why I mention an interesting connection to things like CAP).
Also, I would also love to see a reasonable program that does not have external resources: I find that almost all the work my programs are doing are managing and moving around external resources... from threads to sockets to money, you are probably not doing anything terribly useful unless you are dealing with a non-memory resource.
Regardless, the goal of these statements is "a defense of Objective-C", not "why Objective-C is amazing": the "defense of Java", when you show someone a four-level nested try/finally whose sole purpose is to make non-deterministic finalization of File objects exception-safe, is "but we have garbage collection, which has these nifty properties, including automatic cycle detection".
>I would love to see a reference for the "profound practical difference well-recognized in the decades of work that's gone into memory GC".
I don't think you need a reference - when we discuss algorithms we have a notation to describe an algorithm's behaviour in time and space yet never bicycle messengers. If anything, this suggests these resources are, indeed, somehow (and obviously) more fundamental.
The 'defense of Obj-C' article thing is pretty silly, no argument there.
Unlike other resources, dangling references to memory are catastrophic. They generally go undetected (no layer of indirection to invalidate them) and can arbitrarily corrupt any future object, violate any type safety rules of the platform, and make even bug-free code execute incorrectly. "Is this object really dead?" is the kind of mind-numbing but critically important question that computers demonstrably answer much more reliably than people. That's why even objc uses a simple but expensive form of garbage collection (reference counting) rather than making you declare "not only am I not using this, I hereby bet my reputation that nobody else is either" by calling free().
The idea of type-safety has nothing to do with this: a type-safe language implemented entirely with reference counting, having no ability to write to arbitrary memory locations on purpose or on accident, has this same tradeoff.
Additionally, as I think this is also relevant to your comment, if you take a step back for a second from the notion that an object /is/ memory, and think of memory as being one of the resources that an object is using, things become more clear.
Simply put, we have two common ways of reclaiming objects automatically: garbage collection and reference counting. The tradeoff is that with garbage collection, you get free cycle detection at the cost of not having deterministic finalization.
Each of these options has downsides: neither is fundamentally better than the other, and not having either one causes you to have to scratch your head occasionally, or add extra code to deal with the lack of automation.
(edit:)
Thinking about this overly simple description, I realize that I'm over-simplifying a little too much... a lot of the fundamental problem has to do with an inability to determine the order of finalization of objects in a cycle, which is what causes a lot of mistakes in Java finalizer implementations.
In comparison, the practical problems are that the common implementations involved take extreme positions: if you have a garbage collected language, it normally is not "reference counting with a cycle detector bolted on", which yields a weird property of possibly arbitrary delays on finalization of valuable resources when you aren't under memory pressure.
(on a side note: would be cool if HN had a SO-style notification system which would tell me when someone replies ...)
I think I have a solid understanding of both models and how to work with each, but as far as syntax goes I'm fairly ambivalent (granted, most of my code is in C/assembly).
With this realization, Objective-C is actually really good at this, requiring many fewer (if not actually no) try/catch blocks to get "safe" code than is required on seemingly almost every line of Java (which doesn't even have C#'s "using" to help it out, a primitive that causes its own problems due to lack of contracts), and has conventions that make it quite simple to "locally" (as in, without having to read anything not directly around the area you are analyzing) determine whether code you are looking at is handling things correctly.
Therefore, and I kid you not: when I saw "a defense of Objective-C" I was anticipating an article that was going to start with how awesome the memory management was (listing the auto-release pool paradigm as something that people typically haven't developed into mature C++ memory paradigms), followed by a discussion of the balanced tradeoff between fully dynamic typing and dispatch with low-level performance-oriented code; instead, I read "You’ll have to prise my garbage collecter out of my cold, dead hands!", sigh, and go back to coding. :(