I saw some code for an alternative way to store instance variables for a class (instead of just declaring them in the class’s header). I will show the code and then explain it:
@implementation NSObject(ExtraOutlet)
static CFDictionaryRef externalFooTable = NULL;
- (void)setFoo:(id)value {
LazyInit(externalFooTable, ...); // This just initializes if externalFooTable is nil.
if (value) {
CFDictionarySetValue(externalFooTable, self, value);
} else {
CFDictionaryRemoveValue(externalFooTable, self);
}
}
- (id)foo {
return externalFooTable ? CFDictionaryGetValue(externalFooTable, self) : nil;
}
@end
OK, so what is going on here? Well, let’s say you have a class, and you have a instance variable called foo (and subsequent KVC methods). If you don’t use foo a lot, or if there are very few instances of your class that will have foo be something other than nil, than you can store the value of foo per instance in a static hash table. The key to the hash table per instance is the instance’s address (self in ObjC).
Because of this static hash table, if you have 1,000,000 instances of your class, and you only have one instance that has foo be something other than nil, than you are saving 3.815 MB of RAM! And imagine doing this for a complicated program with lots of classes and instances, you could really have a major performance boost.You can use this method on your own classes, or as the example suggests, add additional instance variables to classes you didn’t write like NSObject, NSString, etc.
Thanks co-worker Jon for teaching me this one.
I just posted the first release build for a command utility I wrote called GPMethods. It used for rapidly generating mutators and accessors for Objective-C. Instead of writing dozens of lines of setters, getters, and dealloc statements, GPMethods takes a list of property names and types, and then depending on the flag you give it, will produce the necessary code that you can paste right into your favorite text-editor.GPMethods is also really smart, and automatically tailors the generated output based upon the types you give it. For example, it will automatically release and retain properties that its thinks are objects, using the correct methodology whether it is a Cocoa, CoreFoundation, or even CoreGraphics object.Download the build at the GPMethods page.
I ran across what to me was not a seemingly obvious Cocoa artifact today. Let’s suppose that you create some strings much in the way I do below:NSString *myString1 = @"Kevin";NSString *myString2 = [[myString1 copy] autorelease];NSString *myString3 = [[[NSString alloc] initWithString:myString2] autorelease];NSLog(@"%p, %p, %p", myString1, myString2, myString3);Strangely, my log prints the following:0×6060, 0×6060, 0×6060meaning that all the objects are in the same address space! I didn’t quite understand how this could be considering the fact that I used three different methods to create a string. I tried the same thing with NSArray, NSIndexPath, and some others. Strange…After asking around, I finally got my response from Michael, a Cocoa veteran down the hall. He said, “try the same thing, but with mutable strings.”NSMutableString *myString1 = @"Kevin";NSMutableString *myString2 = [[myString1 copy] autorelease];NSMutableString *myString3 = [NSMutableString stringWithString:myString2];NSLog(@"%p, %p, %p", myString1, myString2, myString3);And sure enough:0xae60dd, 0xae60f7, 0xae60fd: different objects!So what’s the deal behind this? In Cocoa, when you allocated a new immutable string using any method, it checks to see if that string already exists in memory. If so, instead of actually allocating a new string, it just increments its retain count. You can imagine how handy that when you create 60,000 of the same string, and Cocoa just creates one with a retain count of 60,000.With mutable objects, however, we do not just bump the retain count because of the fact that if two sources create the mutable string “Kevin”, for example, and then one of the sources changes it to “Keeven”, they change in both places. Not a good thing. So mutable objects are actually allocated each time you create one.So watch out when you are doing pointer checking with immutable objects. You might not be getting exactly what you thought!
So I spent a few days trying to find a memory corruption bug, known as the infamous EXC_BAD_ACCESS. Basically that means that you are over-freeing zones of memory. In your GBD console, you will get some message like: *** malloc[705]: error for object 0×9c5b00: Incorrect checksum for freed object - object was probably modified after being freed; break at szone_error. Hmm. That really doesn’t tell us anything.The problem with malloc errors is that enabling zombies really doesn’t do anything, same with setting symbolic breakpoints in szone_error and malloc_error_break. So what’s the solution to finding this?Guard Malloc. Guard Malloc is a hard-to-find memory bug tracking utility that is part of libgmalloc.dylib. When you run with it enabled, for every malloc(), NSZoneMalloc(), or other associated memory allocators, Guard Malloc allocates its own virtual memory page, with the end of the allocated buffer at the end of the page. Therefore whenever you access outside of the buffer, it immediately causes a bus error. In addition, when memory is freed, libgmalloc deallocates its virtual memory buffer, so read and writes to the buffer also cause immediate bus errors. When running from Xcode with Guard Malloc, the Debugger will jump to the source where the bus error occurred making these bugs really easy to find.One issue with Guard Malloc is that because of all the paging, it takes a long time to do any operation. So be prepared to wait.Overall though, Guard Malloc is great and extremely helpful.