Objective-C Memory Management

Objective-C provides 2 types of memory management

  • Reference counting (Memory managed by manual coding)
    NSString* value = [[NSString alloc] init];
    
    [value retain];
    [value autorelease];
    • When an object is allocated, it has a reference count of 1.
    • Developer manually increment or decrement a reference count of an object
    • retain increases the count by 1 while release decrement it by 1
    • Object will be deallocated if the reference count reaches 0
  • Garbage collection (Memory managed by the Objective-C runtime automatically)
    • Objective-C runtime provide option of manage memory automatically with garbage collection
    • No need of manual coding in reference counting
    • Support in Mac 0S X v10.5 or later. Developer should develop application with garbage collection for Mac OS X
    • Not support in iOS
    • Garbage collection is an opt-in feature when compiling the code
    • Objective-C runtime will determine any object that is not reference and free it (garbage collection)

Objective-C Memory Management with Reference Counting

Objective-C defines some basic rules to determine who should release an object. This guideline provides a consistent coding practice of where objects should be released

With reference counting, developer is responsible for release or autorelease an Objective-C object in the following situations:

  • The method creates an object with a method begins with alloc, new, copy, or mutableCopy should also release the object
    - getObject {
      NSString* v1 = [[NSString alloc] init];
      // Delay "v1" de-allocation until the complete method call chain is completed
      [v1 autorelease];
      return v1;
    }
    • autorelease does not deallocate the object immediately. Indeed, it will let the whole method call chain to complete before freeing the memory
    • This gives a chance for the caller to access the object without worrying freeing the objects itself
      id result = [myObject getObject];
      // autorelease in getObject delay the deallocation
      // Hence, it is still valid and accessible here
      [result method1];
      • Do not use release in getObject since it will deallocate the object and make it invalid after the call
  • When an object is set as an instance variable of an object, implement a setter method to manage the reference counts correctly
    • Release the old object and retain the new object in the setter method
      - (void) setValue: (NSString*)input
      {
          [value autorelease];
          value = [value retain];
      }

      Or

      - (void) setValue: (NSString*)input
      {
          [input retain];
          [value release];
          value = input;
      }
    • If the input is the same object as the object to be replaced, the code above will not de-allocate the object pre-maturely
  • In some situation, a method may increment the reference counter with retain
    • It should match with a release or autorelease even not necessary in the same method
      // Increment the reference count by 1
      [s retain];
      ...
      [s autorelease];
  • Deallocate the instance variable object(s) when an object is deallocated
    - (void) dealloc
    {
        [value release];
        [super dealloc];
    }

A object is guaranteed to be valid within the method it was allocated with the exceptions of

  • when an object is removed from a collection, the object is de-allocated
  • when a parent class is released. it's instance variables are also released

Note:

  • Objects or keys added to collections (NSArray, NSDictionary) is retain automatically and released when it is removed from the colltection

Objective-C Autorelease

release de-allocates an Objective-C object immediately and further reference to the object will be invalid. autorelease registers the object in an autorelease pool. An autorelease pool exists in virtually every X-code project that a developer creates. The objects in the autorelease pool will be released only if the pool itself is released, and this is usually done only after the chain of method calls are completed. The delayed release simplify the memory management in returning an object to a caller.

The Application Kit used for a x-code project automatically creates a autorelease pool (an NSAutoreleasePool instance) at the beginning of an event (such as a mouse down event). The pool will be drained once it finished handling the event. This frees from a developer in creating and draining the autorelease pool.

The following sample code demonstrate how to manage an Objective-C object returned from a method

- myCallee {
  ...
  id newObject = [[NSString alloc] init];
  // Before returning an object alloc within this method, it should be autorelease first
  [newObject autorelease];
  return newObject;
}
  • A method owns any objects returned with methods starts with alloc, new, copy, or mutableCopy
  • With the Objective-C memory coding guideline, this method is responsible for release the object also
  • Use autorelease to delay the release such that the caller can still reference the returned object

Caller

- myCaller {
  ...
  id obj = [myObject createANewInstance];
  // obj is still valid even autorelease is called (not true if myCallee make a message call like [newObject release])
  [obj someMehtod];
  // Does not need to call [myObject release];
}
  • obj is still valid because autorelease does not really release the object yet
  • obj will be released when the autorelease pool is released
  • For a x-code project, the autorelease pool is released after the event handling is completed
  • Since the object is not returned with methods starts with alloc, new, copy, or mutableCopy, the Objective-C memory coding guideline says this method is not responsible of releasing it

This applies to convenience constructor also

Create Autorelease pool manually

Application Kit handles the autorelease automatically. If a custom autorelease pool is needed, the following are the sample code to create and to destroy a autorelease pool

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSString* obj = [[NSString alloc] init];
    [obj autorelease];

    [pool drain];

General Practice in Reference Count

 

  • For a local variable

    • If the local variable is returned to the caller, autorelease it before return
    • If the local variable is not returned to the caller, release it before return
  • For a class instance variable, release the instance variable in dealloc

Cautious with Reference Count in using the Setter Method

 

Avoid using init and setter for instance variable together: it will set the reference count to 2 instead of the expected value (1)

Wrong
- method1 {
  ...
  id newObject = [[NSString alloc] init];
  // Wrong. both init and the setter will increment the reference count to 2
  [self setValue:newObject];
  ...
}

Assign the object directly

- method1 {
  ...
  id newObject = [[NSString alloc] init];
  // Set the instance variable directly
  value = newObject;
  ...
}

Or Manually decrease the count

- method1 {
  ...
  id newObject = [[NSString alloc] init];

  [self setValue:newObject];
  [newObject release];
  ...
}

Memory Management for Getter method

Objective-C can create the memory management code automatically with the property declaration

@property (retain) NSDate *value;

If implement manually:

- (NSString*) value {
    return [[value retain] autorelease];
}

Reference Counting for Objective-C Collection Class

for (i = 0; i < 5; i++) {
    NSNumber *n = [[NSNumber alloc] initWithInteger: i];
    [myArray addObject:n];
    [n release];
}
  • addObject will increment the reference count

Garbage collection is not available for iOS

Garbage collection is off by default. To take advantage of automatic garbage collection, compile the code with

-fobjc-gc-only

If code uses garbage collection and reference counting, compile it with

-fobjc-gc
  • Use this for backward compatibility with legacy code only