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.
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
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.
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:didFinish
Launching
WithOptions:
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.