Adventures in the transition from C to Cocoa.

Wednesday, July 18, 2007

Second Nature

I've been writing Cocoa plugins for Quartz Composer (see kineme.net for more details) pretty consistently for the past month or so.

Such continual writing, especially in an already-existing application, has taught me many things, very quickly. It's quite fun; I now wish that I had had the opportunity (or probably just the motivation) to work in such an environment when I was learning C or C++ or even Basic. Immersion is such a powerful learning tool. Not to downplay the importance of theory though; without the theory I've learned from my first languages I would probably be dead in the water in Cocoa.

I'm no expert yet, nor will I be for quite a while (especially with Leopard coming so soon, with its new Xcode and tools), but I am way more comfortable with development, and, perhaps more importantly, with debugging.

So, what direction is there now for this blog? Anyone with determination can push themselves through the differences in Objective-C. I can't offer much more than anecdotal evidence for that.

Perhaps I should just make lots of demos to show how to do various things? Since I have zero readers, I guess this decision is ultimately up to me...

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.

Categories