Adventures in the transition from C to Cocoa.

Tuesday, June 26, 2007

NSAutoReleasePool

NSAutoReleasePool is Memory Management object used in Cocoa. They act as a catch-all for allocations, cleaning up memory when objects are no longer needed.

Instead of sticking strictly to retain and release, these pools allow us one more memory management method: autorelease. This method puts our object in the most recently-created pool. When these pools get freed, all the objects in them get freed as well. Because of this behavior, it's common to put these pools around loops and inside functions that perform several allocations. It is common to create a pool right from the beginning in main(), as a catch-all for your entire application.

Additionally, Cocoa's Frameworks often require a pool to be available for things to work correctly. Without them, the program will probably still run, but it will generate Tons of warnings like this at runtime:


cwright@phendrana:~/projects/addr>./main.broken
2007-06-26 20:43:51.068 main.broken[3819] *** _NSAutoreleaseNoPool(): Object 0x1802000 of class NSCFDictionary autoreleased with no pool in place - just leaking


This is Cocoa's way of telling you that you need an autorelease pool in place to prevent leaks.

Using autorelease pools is quite simple: NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; sets up a pool. [pool release] cleans it up. They can be nested, so that a function can create one, and then a loop in that function can create its own as well. Nested pools get released automatically when their parent pool is released. This is probably very sloppy, but it appears to work.

Here's some sample code that shows this behavior, as well as usage of NSAutoReleasePool.


#import <Foundation/Foundation.h>

@interface OurObject: NSObject
-(void)dealloc;
@end

@implementation OurObject
-(void)dealloc
{
printf("Dealloc on OurObject\n");
[super dealloc];
}
@end

void createAPoolAndLeak()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
OurObject *oo = [[OurObject alloc] init];

// add to this function's pool...
[oo autorelease];
// ... and forget to release the pool! *Gasp*
}

void createAPoolAndDontLeak()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
OurObject *oo = [[OurObject alloc] init];

// add to this function's pool...
[oo autorelease];
// ... and release the pool!
[pool release];
}

int main()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

printf("Creating a pool and not leaking...\n");
createAPoolAndDontLeak();
printf("Pool cleaned up.\n");
printf("Creating a leaky pool\n");
createAPoolAndLeak();
printf("Leaky pool created.\n");

printf("Releasing parent pool...\n");
[pool release];

printf("Pool is cleaned up...\n");
return 0;
}


Using these pools is disappointingly simple. They really don't require much work at all.

AddressBook

Ok, so I lied in my last post. I do have some GUI stuff in the works, but it's taking exceptionally long to screenshot the whole procedure. Sorry.

But, I do have one nugget of sample goodness for you: How to pull data out of the OS X AddressBook.

#import <Foundation/Foundation.h>
#import <AddressBook/ABAddressBook.h>
#import <AddressBook/ABMultiValue.h>

void printMultiValue(char *title,id prop, ABPerson *p)
{
int j,k;
ABMultiValue *multi = [p valueForProperty:prop];

if([multi count])
printf(" * %s:\n",title);

// sometimes we get NSDictionaries for values. These
// need to be handled differently (kABAddressProperty does this, for example)
if([[multi valueAtIndex:0] isKindOfClass:[NSDictionary class]] == YES)
{
for(k=0;k<[multi count];++k)
{
NSArray *keys, *values;
keys = [[multi valueAtIndex:k] allKeys];
values = [[multi valueAtIndex:k] allValues];

// get the dictionary name
printf(" * %s\n",
[[multi labelAtIndex:k] UTF8String]);

for(j=0;j<[keys count];++j)
{
printf(" * %s: %s\n",
[[keys objectAtIndex:j] UTF8String],
[[values objectAtIndex:j] UTF8String]);
}
}
}
else // normal handling
{
for(j=0;j<[multi count];++j)
{
printf(" * %s: %s\n",
//[[multi identifierAtIndex:j] UTF8String],
[[multi labelAtIndex:j] UTF8String],
[[multi valueAtIndex:j] UTF8String]);
}
}
}

int main()
{
int i;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
ABAddressBook *book = [ABAddressBook sharedAddressBook];

NSArray *people = [book people];

printf(" * People count: %i\n",[people count]);

for(i = 0;i<[people count];++i)
{
printf(" * Person %i\n",i);
ABPerson *p = [people objectAtIndex:i];
printf(" * First Name : %s\n",[[p valueForProperty:kABFirstNameProperty] UTF8String]);
printf(" * Middle Name: %s\n",[[p valueForProperty:kABMiddleNameProperty] UTF8String]);
printf(" * Last Name : %s\n",[[p valueForProperty:kABLastNameProperty] UTF8String]);
printf(" * Company : %s\n",[[p valueForProperty:kABOrganizationProperty] UTF8String]);

printMultiValue("E-Mail",kABEmailProperty,p);
printMultiValue("Phone",kABPhoneProperty,p);
printMultiValue("Address",kABAddressProperty,p);

printf(" * Note : %s\n",[[p valueForProperty:kABNoteProperty] UTF8String]);
}

[pool release];
return 0;
}


That's a pretty loaded example. And to compile it from the command line: gcc main.m -o main -framework Cocoa -framework AddressBook. Here we use an AutoreleasePool, which I haven't talk about yet, as well as some NSString data to present some meaningful output to the user.

This program will dump out a ton of information found inside your addressbook. There are a few more fields inside, but this is probably enough to get you started. I'll have to go through and document more sometime.

Tuesday, June 19, 2007

Xcode Examples Coming Soon

Normally I work in command-line land because it's far more familiar to me, and easier to control. There's also naturally less code overhead, so you can focus on what's actually going on without all the extra fluff. That said, any useful application will need a useful user-interface to actually help someone accomplish work.

Xcode is Apple's IDE of choice for developing Cocoa applications. To compliment the 7 trillion screen-shot-laden Xcode blogs out there, I'll shortly be adding my own contribution to the mix.

That said, I intend to focus more on documenting how various GUI-related problems are solved using existing tools. It takes a considerable amount of work to make a well-polished user interface (probably another reason why I avoid GUI-land :), so I'll try to reveal as much polish as I can uncover.

Tomorrow, we'll begin our GUI adventures. Don't worry though, we'll still fall back to command-line for a while at least, to illustrate basic concepts and methods.

Categories