Memory Management for Block Objects

iOS apps run in a reference-counted environment. That means every object has a retain count to ensure the Objective-C runtime keeps it as long as it might be used, and gets rid of it when no one can use it anymore. You can think of a retain count as the number of leashes on an animal. As long as there is at least one leash, the animal will stay where it is. If there are two leashes, the animal has to be unleashed twice to be released. As soon as all leashes are released, the animal is free. Substitute all occurrences of animal with object in the preceding sentences and you will understand how a reference-counted environment works. When we allocate an object in iOS, the retain count of that object becomes 1. Every allocation has to be paired with a release call invoked on the object to decrement the release count by 1. If you want to keep the object around in memory, you have to make sure you have retained that object so that its retain count is incremented by the runtime.

Note

For more information about memory management in iOS apps, please refer to iOS 4 Programming Cookbook (O’Reilly).

Block objects are objects as well, so they also can be copied, retained, and released. When writing an iOS app, you can simply treat block objects as normal objects and retain and release them as you would with other objects:

typedef NSString* (^StringTrimmingBlockObject)(NSString *paramString);

NSString* (^trimString)(NSString *) = ^(NSString *paramString){
  NSString *result = nil;

  result = [paramString
            stringByTrimmingCharactersInSet:
            [NSCharacterSet whitespaceCharacterSet]];

  return result;
};

- (void) callTrimString{

  StringTrimmingBlockObject trimStringCopy = Block_copy(trimString);

  NSString *trimmedString = trimStringCopy(@"   O'Reilly   ");

  NSLog(@"Trimmed string = %@", trimmedString);

  Block_release(trimStringCopy);

}

Use Block_copy on a block object to declare ownership of that block object for the period of time you wish to use it. While retaining ownership over a block object, you can be sure that iOS will not dispose of that block object and its memory. Once you are done with that block object, you must release ownership using Block_release.

If you are using block objects in your Mac OS X apps, you should follow the same rules, whether you are writing your app in a garbage-collected or a reference-counting environment. Here is the same example code from iOS, written for Mac OS X. You can compile it with and without garbage collection enabled for your project:

typedef NSString* (^StringTrimmingBlockObject)(NSString *paramString);

NSString* (^trimString)(NSString *) = ^(NSString *paramString){
  NSString *result = nil;

  result = [paramString
            stringByTrimmingCharactersInSet:
            [NSCharacterSet whitespaceCharacterSet]];

  return result;
};

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{

  StringTrimmingBlockObject trimmingBlockObject = Block_copy(trimString);

  NSString *trimmedString = trimmingBlockObject(@"   O'Reilly   ");

  NSLog(@"Trimmed string = %@", trimmedString);

  Block_release(trimmingBlockObject);

}

In iOS, you can also use autorelease block objects, like so:

NSString* (^trimString)(NSString *) = ^(NSString *paramString){
  NSString *result = nil;

  result = [paramString
            stringByTrimmingCharactersInSet:
            [NSCharacterSet whitespaceCharacterSet]];

  return result;
};

- (id) autoreleaseTrimStringBlockObject{

  return [trimString autorelease];

}

You can also define declared properties that hold a copy of a block object. Here is the .h file of our object that declares a property (nonatomic, copy) for a block object:

#import <UIKit/UIKit.h>

typedef NSString* (^StringTrimmingBlockObject)(NSString *paramString);

@interface GCDAppDelegate : NSObject <UIApplicationDelegate> {
@protected
  StringTrimmingBlockObject   trimmingBlock;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, copy) StringTrimmingBlockObject   trimmingBlock;

@end

Note

This code is written inside the application delegate of a simple universal iOS app.

Now let’s go ahead and implement our application’s delegate object:

#import "GCDAppDelegate.h"

@implementation GCDAppDelegate

@synthesize window=_window;
@synthesize trimmingBlock;

NSString* (^trimString)(NSString *) = ^(NSString *paramString){
  NSString *result = nil;

  result = [paramString
            stringByTrimmingCharactersInSet:
            [NSCharacterSet whitespaceCharacterSet]];

  return result;
};

- (BOOL)            application:(UIApplication *)application
  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

  self.trimmingBlock = trimString;

  NSString *trimmedString = self.trimmingBlock(@"   O'Reilly   ");

  NSLog(@"Trimmed string = %@", trimmedString);

  // Override point for customization after application launch.
  [self.window makeKeyAndVisible];
  return YES;
}

- (void)dealloc{
  [trimmingBlock release];
  [_window release];
  [super dealloc];
}

@end

What we want to achieve in this example is, first, to declare ownership over the trimString block object in our application delegate, and then to use that block object to trim a single string off its whitespaces.

Note

The trimmingBlock property is declared as nonatomic. This means that this property’s thread-safeness must be managed by us, and we should make sure this property won’t get accessed from more than one thread at a time. We won’t really have to care about this at the moment as we are not doing anything fancy with threads right now. This property is also defined as copy, which tells the runtime to call the copy method on any object, including block objects, when we assign those objects to this property, as opposed to retaining those objects by calling the retain method on them.

As we saw before, the trimString block object accepts a string as its parameter, trims this string, and returns it to the caller. Inside the application:didFinishLaunchingWithOptions: instance method of our application delegate, we are simply using dot notation to assign the trimString block object to the trimmingBlock declared property. This means that the runtime will immediately call the Block_copy on the trimString block object and assign the resulting value to the trimmingBlock declared property. From this point on, until we release the block object, we have a copy of it in the trimmingBlock declared property.

Now we can use the trimmingBlock declared property to invoke the trimString block object, as shown in the following code:

NSString *trimmedString = self.trimmingBlock(@"   O'Reilly   ");

Once we are done, in the dealloc instance method of our object, we will release the trimmingBlock declared property by calling its release method.

With more insight into block objects and how they manage their variables and memory, it is finally time to move to Chapter 2 to learn about the wonder that is called Grand Central Dispatch. We will be using block objects with GCD a lot, so make sure you have really understood the material in this chapter before moving on to the next.