Hacker News new | past | comments | ask | show | jobs | submit login
Objective-C Memory Management For Lazy People (interfacelab.com)
96 points by jawngee on Dec 9, 2010 | hide | past | favorite | 35 comments



I think the article vastly overstates its case. Nobody disputes that it's possible to have more-or-less working memory management in ref-counted Objective-C. But it's clearly harder than in a GC language, else it wouldn't need seven pages worth of "simple rules" to explain.

Moreover, the author glosses over many of subtleties of reference-counting. For example, accessors in Java are dead-simple, practically boilerplate. But in Cocoa, what looks like a perfectly reasonable implementation to a newcomer is in fact buggy:

  - setFoo: (id)aFoo {
    [foo release];
    foo = [aFoo retain];
  }
Look closely, and you'll realize that if foo and aFoo are in fact the same object, it will be freed, and foo will have an invalid pointer assigned to it. Pretty much every Cocoa programmer knows about this, but we shouldn't ahve to.

One of the better Cocoa blogs is written by Mike Ash, and he has quite a few posts that illustrate some of these issues. For example, the first half of http://www.mikeash.com/pyblog/friday-qa-2010-12-03-accessors... talks about whether or not accessors should autorelease their return values, with pros and cons for each option. Later on, the money quote: "If you're using garbage collection, this whole question becomes vastly simpler."

Our author here says that "Cyclic object graphs in Objective-C are not a problem. At all." But another post by Mike Ash illustrates that it can be harder to deal with: http://www.mikeash.com/pyblog/friday-qa-2010-04-30-dealing-w... . A great example:

  _myInstanceVar = [[SomeClass alloc] initWithBlock: ^{
    [self doSomething];
  }];
Using 'self' within the block captures it in the closure as a reference, and then the closure itself is referred to by the instance variable of 'self'. This cycle is hard for the coder to notice, and will leak.


People should really use @property syntax for accessors, unless they need finer control over what happens. http://developer.apple.com/library/mac/#documentation/Cocoa/...


Here's another good example of why memory management isn't as simple as this article makes it out to be

    -(void) addAccount:(UIBarButtonItem*)button {
        ListController *list = [[ListController alloc] initWithStyle:UITableViewStylePlain];
        UIPopoverController *pop = [[UIPopoverController alloc] initWithContentViewController:list];
        pop.popoverContentSize = CGSizeMake(320, 200);
        [pop presentPopoverFromBarButtonItem:button permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
        [list release];
        [pop release];
    }
Anyone see the problem with this code?


UIPopoverController does not retain itself and will cause a crash shortly after that release. Both the iOS SDK and the Cocoa SDK are fairly inconsistent about which presentation classes require retaining and which handle that themselves -- UIAlertView does not require it, for example.


Precisely, and that's the thing I've come to learn about objective-c memory management. It's fairly easy to learn as the article points out, but in practice when you have to interface with other developers code, it can be a confusing mess.


I know this from practice, having come in halfway through a project on iOS whose code was exquisitely, terrifyingly awful. I was fine on memory management; it was hunting down previous memory management errors that took up most of my time. I ended up giving up and reimplementing certain classes because it was seriously simpler than debugging the existing code.

I think the issue is that trivial examples—for example, the kind you have in a blog post or comment—will invariably look simple and easy. In a non-trivial application, "four basic rules" ends up being four more things that can create bugs.


And this brings up a point overlooked in the article: it isn't about the time it takes to do things right, it's also about the time it takes to track down the problem when you inevitably (you ARE human after all) make a mistake. To me, that's the real value of GC, because memory allocation errors are tedious and time consuming to debug.

The author is confusing 'habitual' with 'easy', and the two concepts are very distinct. And even beyond that he undervalues the debug time saved with automated GC.


Well, before knowing about Obj-C memory management in detail I thought it was ok. Now that I know the rules it looks painful.

If someone were to write such an article about C, then it would have just one line: if you malloc, then you must free.

If someone were to write such an article about C++, it would be more involved: delete if you new, delete[] if you new[] and then they'd explain auto_ptr and shared_ptr. More complicated that C, but still reasonably easy and you can get automatic memory management.

On the other hand we have Objective-C, where you apparently need at least 11 paragraphs to explain the basics, only to find out that - yes - you still have to call malloc and free only with other names and slightly different behaviour?


For me "if you malloc, then you must free" is much harder, because you have to know whether ownership was passed to you, who else might be using object when you free it, etc.

In Objective-C there's no owner, everyone borrows objects. You increase retain count (if you want to hold object for longer than lifetime of a function), and you decrease retain count when done.

And there are few methods to increase (retain, init, copy, new, property assignent) and few to decrease count (release, autorelease, nil property assignment).

To me it's that simple.


Reference count based GC? How would you deal with circular reference?


You don't.

You'd avoid creating circular structures as much as possible. You can get pretty far that way.


It's very difficult to avoid it when thing goes complicating. A may reference B, B may reference C, ... and somewhere, X may reference back to A. If this strategy makes sense for big project, many hackers work with C++ can use it either, as the performance overhead is very low.

But in the real world, reference-count based GC just has limited usage.


> But in the real world, reference-count based GC just has limited usage.

And in theory it doesn't fare much better. Proper garbage collection can have better performance, and does the Right Thing.


Retain your children but not your parents; or do you mean a group of "peers" who retain each other? They should probably be in some kind of collection that retains them All.


The article is written in a condescending manner because the author is irritated. This means that it drags on a bit because of all the hand-holding through mundane examples.

The rules are just as simple as C or C++, and Apple explains them far more succinctly:

http://developer.apple.com/library/mac/#documentation/Cocoa/...


I don't mind that the author is condescending. However, I do not understand why C memory management is widely considered as difficult (there's a big trend to support GC nowadays to escape the "evil of malloc"), but Obj-C memory management is considered by the author as easy.


In ObjC I don't need to worry about whether someone else is using an object when I release it. Releasing an object doesn't free it unless no one else is retaining it.

Dealing with this sounds like a nightmare in C.


Reference counting solves a number of otherwise difficult problems with the basic memory management of C. Yes, malloc and free are very simple operations, conceptually, but they are so low level that attempts to create a complex system without putting a layer of memory management over the top are generally destined to fail. Note that if you want, reference counting can be reduced to malloc/free - you simply use alloc/release instead of malloc/free - it's a direct substitution. But reference counting helps you for use cases that are more difficult.

Here's a real life example that I ran into a few years ago when trying to write a largish system using just malloc and free. It was in the world of Conditional Access, and I was doing a cardless system that used a secure tunnel to talk to a server. I had an object that represented the Service Operator, and this object created the secure tunnel to talk with the server representing the service operator at the head-end. It worked just fine most of the time until I tried to handle the case of the command that deleted the representation of the Service Operator in my set top box. When I called Delete on the Service Operator object, my secure tunnel blew up, because it relied on information that was held by the Service Operator object. What I needed was a way of being able to say Delete the Service Operator object, but wait until the tunnel has closed before doing it.

Now, you could just set a special flag in the tunnel object saying "delete the service operator after closing", or you could create a Service Operator callback that is called when the tunnel closes, and it doesn't act on any request to delete until that callback is received - but then what happens if someone other than the tunnel wants to delete the service operator? The callback never comes. It was a version of this system that I used in the end, but it was far more complicated than it would have been with reference counting. The solution when using reference counting is simply to a retain of the ServiceOperator when the tunnel opens, do a release when the tunnel closes, and if the tunnel really wants to delete the service operator, it does a release in mid-communication. As simple as can be.


Thanks. Obj-C is better at sharing objects then. What if I create a custom malloc/free that keeps a share count at the beginning of the memory block, would that be similar?


Objective-C memory management is still an abstract concept that has, as the root of it's mechanism, objects. You create an object, you release an object.

C's memory management has a simplistic interface: malloc/free. However it operates on the notion of raw memory space. You need to do more work figuring out how much memory you need based on the size of your objects.

C's memory management is not difficult, but it is more involved. The author says it pretty well when he states that Obj-C hits a middle ground between GC and manual memory management. It elevates the control of memory to a level where it makes more contextual sense.


> If someone were to write such an article about C, then it would have just one line: if you malloc, then you must free.

That implies you cannot return malloc'ed memory to a caller in C. I doubt many people are willing to live under that restriction.


Many C APIs do exactly that: they require you (the caller) to malloc your own memory, then pass in a pointer.


The author didn't do a clear job explaining Obj-C's memory management. Apple on the other hand does:

http://developer.apple.com/library/mac/#documentation/Cocoa/...

--------------------

In simplistic form:

A) If a method "begins with" alloc or new, you must release or autorelease.

B) If a method "contains" copy, you must release or autorelease.

C) For every retain message you send to an object, you must balance it out with a release or autorelease (e.g. malloc/free).

D) If a method returns an object to you, and the method name did not match with the "A" or "B" rules, you have received an autoreleased object. This object is guaranteed to exist for the remainder of its identifier's scope.

E) To make an autoreleased object live longer than its identifier's scope, retain it (ensuring to release/autorelease it when finished).

--------------------

There are a few edge scenario's too (described in the link), but that's the gist of it. Also, whenever you put an object inside an NSSet/NSArray/NSDictionary, it's sent a retain message. When said NSSet/NSArray/NSDictionary gets deallocated, it sends a "release" message to all of its members. A lot of other objects act similarly (always documented well by Apple).

At first it's a little challenging to grasp. But after programming for a little while in Obj-C using the Foundation or Cocoa API, you get the hang of it and it comes quite naturally.

In general I've found learning Obj-C with Foundation/Cocoa is difficult at first, mostly from trying to grasp the design patterns Apple has used with OOP. For example, delegate methods, bindings (using KVC/KVO), the responder chain, etc... But after you've learned them, you go "ah ha!!!" and get it. Then things become much easier to code.

On top of that, I have nothing but good things to say about other Apple coders. From bloggers, to StackOverflow users, to IRC; everybody is extremely helpful and pleasant to deal with. :)


A car is a horse only with other name and different behavior.

If you new, alloc or copy, you own the object and must release it. Don't take ownership of delegates. That's about it.


The article complicates things by talking about 'autorelease' and '@property'. Neither of these concepts are necessary in understanding memory management in ObjC, they just add convenience to it.


There are many, many methods that return autoreleased objects, which must be retained if you don't want your app to go boom, so I'd argue that autorelease should be covered. At least to the extent of explaining this.

autorelease was added at the same time as the retain/release. @property, on the other hand, came much, much later and is entirely separate.


I was just pointing out that you don't need to know autorelease is used if you just know to retain anything that isn't a new/init/copy/etc. Also, you don't need to use autorelease, you can just have all your variables return copies.

Obviously it's something any Cocoa developer would want to learn, but it didn't seem necessary in an article trying to prove how basic memory management in ObjC is.


This article has some good points for people who are new to Objective-C, but (much like the comments you complained that people were downvoting) the snarkiness and condescension tends to distract from it.

Anyway, thanks for posting it. FWIW, I'm one of the comments you linked to as being unhelpful.


The reason most people find Objective-C memory management hard at first is they don't realise the importance of recognising and following the conventions.

Once you know the handful of conventions the article covers, knowing when to retain or release is pretty easy.


The original article was strange in that it got a whole lot of things wrong, but was sufficiently technical that you would actually have to have been an iOS and an Android developer in order to discuss it. This is why the comments were so content-free on Hacker News.

(Just one example of the things the article got wrong: The Android emulator actually defers a lot to the host OS and its own implementations to the point where I have never seen an OpenGL ES app that runs the same way on the emulator as it does on real hardware; the Android emulator also runs at full speed: it's just slower because it's doing more.)


> @property (retain) NSString *title;

Why is he retaining NSString? Shouldn't that be (copy)?


Not necessarily. If you want to avoid situations where title points at a mutable subclass, yes, but if that isn't a concern, there's nothing inherently incorrect with a simple retain.


"copy"ing an immutable string just retains it, so there's little benefit to using a retain property for NSString properties, or for properties of the other NSMutableCopying type families. As a best practice you should always use copy properties for them.


Except that if someone modifies the string they passed you, your 'copy' of the string will also change.


That would only occur if it was an NSMutableString, like the GP said.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

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

Search: