Swift & Objective-C

Objective-C Memory Management History: From MRC to ARC

If you work on iOS development, there’s probably something you’ve wondered about at least once.

5 min read
Cover image for Objective-C Memory Management History: From MRC to ARC

If you work on iOS development, there’s probably something you’ve wondered about at least once.

“Why is there so much retain and release in old Objective-C code?”

If you started with Swift, opening old code may well have left you puzzled.

Today, let’s trace the history of Objective-C memory management—from MRC to ARC.

Here’s the key point first.

Objective-C memory management moved from the era when developers counted manually (MRC) to the era when the compiler counted for them (ARC).

Garbage collection was briefly tried along the way, but ARC ultimately appeared in 2011 and settled the matter.

Let’s walk through each era below.


What Is Reference Counting? The Basics of Memory Management

Before getting into the history, let’s establish one concept.

It’s the reference count.

Think of every object as having a number that counts how many people are holding on to it.

It stays alive while the number is at least 1, and is deallocated when it reaches 0.

So you increment the number when retaining an object (retain), and decrement it when letting go (release).

This simple rule is the foundation of Objective-C memory management.

How a reference count determines whether an object lives or dies
How a reference count determines whether an object lives or dies

The MRC Era: When Developers Counted Manually

Before ARC, during the MRC (Manual Retain Count) era, people managed this number by hand.

Old code filled with retain and release calls—it’s confusing at first glance
Old code filled with retain and release calls—it’s confusing at first glance

The rules were clear. People often memorized them with the term “NARC.”

  • I’m responsible for objects created with New, Alloc, Retain, or Copy
  • I must release objects I’m responsible for when I’m done with them

Actual code looked like this.

// When an object is created, the reference count becomes 1
NSObject *obj = [[NSObject alloc] init];
[obj retain];   // count 2
[obj release];  // count 1
[obj release];  // count 0 → deallocate memory

That’s where the problems began.

If you forgot release, memory leaked; if you deallocated too early, touching an object that had already disappeared crashed the app.

The infamous “zombie object” crash.

You had to keep counting in your head how many references the object you were holding had, which was exhausting.


autorelease: “I’ll Let Go a Little Later, Not Now”

The MRC era had one more awkward situation.

It happened when a method created a new object and returned it to the caller.

- (NSString *)greeting {
    NSString *msg = [[NSString alloc] initWithString:@"Hello"];
    return msg; // I alloc created it, so I release should, but when should I?
}

By the rules, the side that calls alloc must perform release.

But if you release before returning, the object disappears before the recipient can use it. If you don’t, you leak memory.

autorelease was introduced to solve this dilemma.

It schedules the object to be released “not right now, but a little later.”

An autoreleased object is placed in a waiting list called an autorelease pool, and is released when the pool is drained—usually after one run-loop iteration.

This let the recipient safely receive and use the object.

Convenience constructors such as [NSString stringWithFormat:] that returned objects without alloc all used this approach.

This autorelease pool survived into the ARC era as the @autoreleasepool block. It’s a practical tool for memory spikes in loops, so I covered it separately in a longer article.


Why Did Garbage Collection Fail?

Apple knew about this inconvenience too.

So in 2007, Mac OS X 10.5 Leopard introduced garbage collection (GC), which automatically cleaned up memory like Java.

It seemed convenient because developers no longer had to write retain or release.

But the results were underwhelming.

Because GC ran in the background, apps could briefly freeze at unpredictable moments. More importantly, it was a burden on iPhone hardware, where performance and battery life mattered.

As a result, it was never introduced on iOS.

Mac GC was eventually deprecated starting with OS X 10.8 in 2012.


The Arrival of ARC: Letting the Compiler Count for You

Then, in 2011, Apple introduced ARC (Automatic Reference Counting) as the solution.

It was released with Xcode 4.2, iOS 5, and OS X 10.7 Lion.

ARC’s idea was clever.

Keep reference counting, but have the compiler insert retain and release automatically instead of making developers do it.

Unlike GC, which runs a separate cleanup process at runtime, ARC inserts deallocation code exactly where needed at compile time.

This automated memory management with almost no runtime performance overhead.

Here’s a comparison of the two eras.

Category MRC ARC
Introduction Early period–2011 2011 (iOS 5)
retain/release Written manually Inserted automatically by the compiler
autorelease Called directly Managed by the compiler
Risk of memory leaks High Greatly reduced
Performance overhead None Almost none

ARC isn’t magic, though.

Because it cannot automatically resolve retain cycles, developers must break them with weak references such as weak or unowned.

This concept carries over directly to the Swift we use today.


Frequently Asked Questions (Q&A)

Q. Do I still need to learn MRC?

You’ll rarely write new MRC code in practice. Still, older libraries and interviews may test the concept, so it’s worth understanding the principles.

Q. Is autorelease used with ARC too?

You won’t call it directly, but the mechanism still operates under ARC. That’s why the @autoreleasepool block remains useful today.

Q. Does Swift use ARC too?

Yes, Swift is also based on ARC. That’s why retain cycles and the weak concept are equally important in Swift.


In short, Objective-C memory management evolved from “MRC counted by hand → a brief GC experiment → ARC counted by the compiler.”

The automatic memory management we take for granted today was refined through countless crashes and iterations.

I hope this overview gives those studying iOS development a small map. Happy coding!


References