Reference-counting caveats in Objective-C? - objective-c

I've long considered myself a garbage collection snob – despite a secret love for C++, I find myself sneering at developers who actively choose to use languages without (read: missing) garbage collection when they're given the option.
And then I met Objective-C. Wow! Its system of reference counting seems brilliantly simple – I'd even go so far as to say elegant. When developing for OSX, developers are given the option to use a snazzy GC; when developing for iOS, developers are stuck with reference counting.
My question is:
If I am developing an OSX application that could potentially be ported to iOS, is Objective-C's reference counting system time-consuming enough (development-wise and bug-fixing-wise) to warrant ignoring it for the application's first version?
What problems am I likely to run into if I rely on reference counting*, assuming I'm not clever enough to construct any diabolically complex cyclical data structures? With features like autorelease, it all seems so easy, but I know that Apple wouldn't have invested the effort into creating a garbage collector if this were really the case. What should I be on the lookout for?
* I am aware that I can use the garbage collector even if I am throwing around retains and releases (they'll be ignored). However, considering non-GC applications often use RAII, I don't understand how that would work if a generational GC were to "replace" calls to retain and release. Wouldn't resources potentially be released late?

My experience with developing code to port to iOS is that taking GC only code and back porting it to reference counting is a bit tedious and time consuming and potentially error prone. Having said that, as long as you use properties (make them retain even though it makes no difference in GC) as much as possible and you enable the static analyser build phase, it's not too bad. The static analyser will catch most failures to observe the memory management rules. It won't notice if you fail to release an ivar in dealloc, but you can go through and systematically add all the dealloc methods.
Bear in mind that you can't directly port a Mac application to the iPhone, the VC part of MVC has to be completely rewritten, so you could take the approach of writing the Mac UI solely for garbage collection and only make the model classes compatible with both GC and reference counting.

Related

As a new Objective-C developer, what memory-related issues should I watch out for using ARC?

Recently I've begun to code in Objective-C for iOS 5 devices. My brand new MacBook is loaded with Xcode 4.2 and the latest Mac & iOS SDKs. So far it's been a fun experience but there is one problem that I see with the current state of documentation and available books.
Specifically, most books (that have yet to be updated) always reference how and when to manage your memory. This is great, however, the current SDK/compiler includes Automatic Reference Counting and since I leave this turned on for my projects, I have no clue as to what I should personally monitor and manage myself.
I come from a C# background. Memory management in C# (technically, .NET) is entirely handled by the framework garbage collector. I understand that ARC is actually a compiler feature that automatically adds boiler-plate code where it belongs. Furthermore, my attempts to try and discover where I should manage my own releasing of objects has caused nothing but compiler errors because ARC wants to take care of it for me.
I have yet to find a case where I've needed to manage my objects. I am becoming "lazy" because I don't know what to monitor and release myself and I am completely oblivious about how this behavior could affect the performance of my application.
In new-user terms, what "gotchas" should I be aware of while using ARC in my iOS projects? I've read a few questions regarding memory management and ARC around here but, to be honest, they are not to friendly to the new iOS developer. Could someone please give a reasonable, bullet-point list that explains what problems and issues to watch out for, as well as a fair guide as to when self-management of memory is necessary?
Circular References. When objects are codependent, they will leak. You will need to mark some references as weak, and Instruments can help you locate these references. These leaks won't even show up as leaks because they hold strong references to each other.
Create Autorelease Pools #autorelease to keep autorelease pool sizes down where you create many autoreleased objects (directly or indirectly). Specifically, your program and programs you depend on will autorelease many objects (ARC or otherwise). An autoreleased object is one which will be released "in the future". Every Cocoa program expects an autorelease pool to exist on each thread. This is why you create a new pool when you create a new thread, and why you create one in main. The pools operate as a stack - you may push and pop pools. When a pool is destroyed it sends its deferred release message to every object it holds. This means that really large loops with many temporary allocations may result in many objects which are referenced only by the pool, and the pool may grow very large. For this reason, you drain manage pools directly in some cases to minimize the number of objects that are waiting to be released and deallocated.
Use proper bridging/casting. Sometimes you will need to manage lifetimes explicitly. ARC handles the obvious cases, but there are complex cases where you will need to manage lifetimes explicitly.
When using malloc'ed and new'ed allocations, as well as opaque types in 'Core' APIs. ARC only manages NSObject types. You still need to explicitly free, delete, and use correct ref counting for these allocations, types, and when interfacing with those APIs.
Always follow NS-API naming conventions, and avoid explicit memory management attributes where possible.
You'll obviously need MRC when you compile sources without ARC or GC. This is quite common when using/working with other libraries/code bodies. Of course, the compiler handles interactions correctly so your ARC program should not leak.
ARC handles most of what you will need if you use proper naming and written style, but there will be a few other corner cases. Fortunately, you can still run Leaks and Zombies to locate these issues if you don't realize them during development.

Starting with Objective-C: To ARC or not to ARC?

According to Apple's ARC Documentation, there are a fairly significant number of changes to the way that one develops software when using ARC.
As a complete beginner to Objective-C, would it be better to start off with ARC disabled, with the idea that it would give me a better low-level understanding of what is going on behind the scenes? Or has ARC essentially deprecated the 'old way' of doing things, to the point that it's not really worth spending time learning?
This is basically an opinion question, and is therefore fairly dangerous.
My Opinion is a qualified yes. It is worth learning basic memory management. The qualification being don't get bogged down in it. Learn what ARC is doing for you under the hood with some very simple projects. Once you have a basic understanding of how to handle memory management, i.e. how to avoid retain cycles(as jemmons alluded to they can still be a problem with ARC).
Once you have a basic grasp of memory management. Start using ARC.
Also as Jason Coco pointed out ARC handles memory management for (to put it simply) NSObject subclasses. So all of the CF Objects you will still be handling yourself, if you need to use them.
An excellent explanation about what ARC is doing for you under the hood can be found in the WWDC2011 Session 323 - Introducing Automatic Reference Counting.
But there are some other considerations that might steer your decision.
What devices do you need to target?
If you plan to target iOS 4.3 and up ARC effectively handles memory management for you.(of NSObject subclasses)
If you plan to target iOS 4.2 then you will not be able to use weak references(you will use unsafe_unretained). iPhone 3g? & iPod touch 2nd gen are stuck at this OS level, because there are many of these devices still in service many developers are still targeting them.
If you plan to target iOSs earlier than 4.2(This would be rare) you will definitely need to learn MRC(Manual Reference Counting).
If you plan to build Mac Apps, there is a garbage collector available on that platform. ARC is also an option(full ARC 10.7, no weak support 10.6).
It's worth noting that ARC is checked by default when you start a new project in Xcode. This is as good a sign as any that the old retain/release way of doing things is deprecated and Apple sees ARC as the future. Your first lesson as a new ObjC developer might be that it never pays to swim up-stream against Apple.
Also, while it's fairly easy to convert old retain/release samples to ARC (for the most part, just drop any retains, releases, and autoreleases), the inverse is not true. And I've already seen a lot of sample code cropping up that's written ARC-style. So as someone just starting out, it'd pay more to learn the ARC way.
Note this doesn't mean you don't have to understand reference counting. It's still an important part of an object's life cycle and you still need to be aware of such things (if only to know when to use weak or strong references). But when it comes time to write code, write it with ARC on.
The "old" style is simply just reference counting to manage your object's lifetime. There's not really all that much too it, but it can be error-prone and cause all kinds of grief. If you're just starting, I'd personally suggest you just learn to program using ARC. You'll still get to deal with reference counting when you need to use the C-library objects, like CoreFoundation or CoreGraphics.
Apple itself recommends ARC for new projects.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html#//apple_ref/doc/uid/10000011i
read point 2 in At a Glance
There is sooo much more interesting to learn in Objective C & iOS apart from the memory management.
My advice : Don't bother about MRR
It would be a great idea just to go over it and understand what's going on, but for development it's not essential, and as you can see most if not all developers have moved their deployment targets to iOS 5.0+, so you are likely not to develop under manual reference counting.
However if you plan to use non ROP -retainable object pointers- in your code like CFStringRef, you might want to really look at non ARC so you can understand things like bridge, because you can combine ARC and non ARC code in one project.

Objective-C ARC vs. MRR: why the switch?

I am a new cocoa developer coming from a C#/Java background. I had been introduced to memory management patterns which objective-c language uses and I just find them very helpful for code-conscious development.
Why does Apple now want us to use ARC (Automatic Reference Counting) instead of MRR (Manual Retain-Release) and what advantages other than time saving does ARC offer?
I see such a transition negatively affecting good-citizen habits that developers gain from the obj-c ecosystem.
Nick
It's unfortunate that making it easier for competent developers to be correct has a side effect of making it easier for new developers to not learn, but it seems like it's probably worth it.
ARC is less forgiving than a tracing collector like C# or Java use though. If you don't have a clear object ownership model you will almost certainly create cycles and leak tons of memory. My hope would be that this is made obvious enough (via Instruments? That still requires seeking it out... not sure what could be done here) that new developers will quickly learn to keep their object graph clear and acyclic.
ARC allows me to concentrate on writing useful code instead of boilerplate dealloc methods.
Most people I know have used autorelease behind every alloc anyway, because it saved you a release later and you couldn't forget to actually put it. So the object was around until the autorelease pool was drained, and with ARC the object gets deallocated when it isn't needed anymore. I think in those cases the ARC-compiled program will even use less memory.
And, shame on me, it helps me to make my apps crashing less often too.
That premature release that happens every 10.000 launches. The one that I could never track down completely, hopefully with ARC this is a thing of the past.
I see such a transition negatively affecting good-citizen habits that developers gain from the obj-c ecosystem.
probably in the same way an embedded developer who started with assembler thinks people that start with C and have never used assembler get into bad habits.
In my opinion the MRR vs ARC discussion is similar.
ARC and C both allow to write more maintainable code in a shorter time.
And both of them can lead to a larger memory and cpu footprint.
If I remember correctly Apple announced that they did add a little speed up to retain and release to compensate that impact on cpu usage. And because of that there is no real reason that MMR is still around.
I, for one, welcome our new ARC overlords.
i've actually rather enjoyed moving to ARC - it's one less thing to think about. on top of that, i think newcomers would often be tripped up by the significance of naming conventions (+alloc, -copy etc vs [NSString stringWith....]). the only tricky bit is when you start dealing with CoreFoundation etc (the C APIs) where you still have to keep in mind just who is owning what.

Should I use ARC in objective-c?

Looking at the release notes of OS 10.7, there's some stuff about using Automatic Reference counting (ARC) to make memory management simple. I'm about to embark on a new Cocoa project soon and wondered whether it would be worth using the model (the way I understand it, you can't mix between using ARC and not using it)?
The dilemma seems to be using something new versus using something that could potentially save lots of debugging time later. But is that the case? Has anyone played around with it and found no real-world benefit?
Without a doubt you should use ARC. ARC injects the retain release calls at the most appropriate times, so you may actually see lower average memory use than you would if you didn't use ARC.
I have used ARC and it is immensely powerful. You stop having random crashes and your app just feels more responsive. As they said at WWDC, there is no reason not to use Automatic Reference Counting.
Also, you can use non-ARC files in the same project as ARC files.
I agree with FreeAsInBeer about using ARC. However, it should also be made clear that garbage collection (GC) should not be used. It looks like Apple is henceforth going to focus their efforts ARC, while letting GC die a slow death. This is wonderful because ARC is clearly a far superior technology, whereas GC is slow and extremly buggy.
So yes. Use ARC. Stay away from GC.

Avoiding, finding and removing memory leaks in Cocoa

Memory (and resource) leaks happen. How do you make sure they don't?
What tips & techniques would you suggest to help avoid creating memory leaks in first place?
Once you have an application that is leaking how do you track down the source of leaks?
(Oh and please avoid the "just use GC" answer. Until the iPhone supports GC this isn't a valid answer, and even then - it is possible to leak resources and memory on GC)
In XCode 4.5, use the built in Static Analyzer.
In versions of XCode prior to 3.3, you might have to download the static analyzer. These links show you how:
Use the LLVM/Clang Static Analyzer
To avoid creating memory leaks in the first place, use the Clang Static Analyzer to -- unsurprisingly -- analyse your C and Objective-C code (no C++ yet) on Mac OS X 10.5. It's trivial to install and use:
Download the latest version from this page.
From the command-line, cd to your project directory.
Execute scan-build -k -V xcodebuild.
(There are some additional constraints etc., in particular you should analyze a project in its "Debug" configuration -- see http://clang.llvm.org/StaticAnalysisUsage.html for details -- the but that's more-or-less what it boils down to.)
The analyser then produces a set of web pages for you that shows likely memory management and other basic problems that the compiler is unable to detect.
If your project does not target Mac OS X desktop, there are a couple of other details:
Set the Base SDK for All Configurations to an SDK that uses the Mac OS X desktop frameworks...
Set the Command Line Build to use the Debug configuration.
(This is largely the same answer as to this question.)
Don't overthink memory management
For some reason, many developers (especially early on) make memory management more difficult for themselves than it ever need be, frequently by overthinking the problem or imagining it to be more complicated than it is.
The fundamental rules are very simple. You should concentrate just on following those. Don't worry about what other objects might do, or what the retain count is of your object. Trust that everyone else is abiding by the same contract and it will all Just Work.
In particular, I'll reiterate the point about not worrying about the retain count of your objects. The retain count itself may be misleading for various reasons. If you find yourself logging the retain count of an object, you're almost certainly heading down the wrong path. Step back and ask yourself, are you following the fundamental rules?
Always use accessor methods; declare accessors using properties
You make life much simpler for yourself if you always use accessor methods to assign values to instance variables (except in init* and dealloc methods). Apart from ensuring that any side-effects (such as KVO change notifications) are properly triggered, it makes it much less likely that you'll suffer a copy-and-paste or some other logic error than if you sprinkle your code with retains and releases.
When declaring accessors, you should always use the Objective-C 2 properties feature. The property declarations make the memory management semantics of the accessors explicit. They also provide an easy way for you to cross-check with your dealloc method to make sure that you have released all the properties you declared as retain or copy.
The Instruments Leaks tool is pretty good at finding a certain class of memory leak. Just use "Start with Performance Tool" / "Leaks" menu item to automatically run your application through this tool. Works for Mac OS X and iPhone (simulator or device).
The Leaks tool helps you find sources of leaks, but doesn't help so much tracking down the where the leaked memory is being retained.
Follow the rules for retaining and releasing (or use Garbage Collection). They're summarized here.
Use Instruments to track down leaks. You can run an application under Instruments by using Build > Start With Performance Tool in Xcode.
I remember using a tool by Omni a while back when I was trying to track down some memory leaks that would show all retain/release/autorelease calls on an object. I think it showed stack traces for the allocation as well as all retains and releases on the object.
http://www.omnigroup.com/developer/omniobjectmeter/
First of all, it's vitally important that your use of [ ] and { } brackets and braces match the universal standard. OK, just kiddin'.
When looking at leaks, you can assume that the leak is due to a problem in your code but that's not 100% of the fault. In some cases, there may be something happening in Apple's (gasp!) code that is at fault. And it may be something that's hard to find, because it doesn't show up as cocoa objects being allocated. I've reported leak bugs to Apple in the past.
Leaks are sometimes hard to find because the clues you find (e.g. hundreds of strings leaked) may happen not because those objects directly responsible for the strings are leaking, but because something is leaking that object. Often you have to dig through the leaves and branches of a leaking 'tree' in order to find the 'root' of the problem.
Prevention: One of my main rules is to really, really, really avoid ever allocating an object without just autoreleasing it right there on the spot. Anywhere that you alloc/init an object and then release it later on down in the block of code is an opportunity for you to make a mistake. Either you forget to release it, or you throw an exception so that the release never gets called, or you put a 'return' statement for early exit somewhere in the method (something I try to avoid also).
You can build the beta port of Valgrind from here: http://www.sealiesoftware.com/valgrind/
It's far more useful than any static analysis, but doesn't have any special Cocoa support yet that I know of.
Obviously you need to understand the basic memory management concepts to begin with. But in terms of chasing down leaks, I highly recommend reading this tutorial on using the Leaks mode in Instruments.