This afternoon, my current roommate Andrew Leman, our friend Christian, and myself went to look at a three-bedroom apartment at 1007 W Clark in Urbana. It certainly exceeded my expectations, and was more spacious and well-kept than I had anticipated. The company we are renting from is in the midst of remodeling, so the apartment will even be nicer for next year. If you would like to see pictures of it, check them out here, or visit the Photos page.
Archive for November, 2007
Pictures from Leslie and I’s Matthiessen State Park trip two weekends ago are up. Check them out here or go to the link on the Photos page.
As of Tiger, a new NSDateFormatter has been introduced to Foundation, and within it are a few things that might take some getting used to. So I will outline the date formatter in its entirety, and how to understand how all of the pieces work together.There are two main modes, or behaviors, of the NSDateFormatter: Pre 10.4 and 10.4+ (these same labels are used in Interface Builder).
- Pre 10.4 Mode (
NSDateFormatterBehavior10_0)
This mode was the only mode until Tiger was introduced, and it makes use of the thestrftime-style format strings. Thestrftimeformat identifiers look like “%S” (seconds), “%m” (month number), and “%d” (day of month) to just name a few. Actually, the documentation for these identifiers is accurately described in Apple’s documentation that comes with Xcode or is found online at the ADC website.UsageSo how might I go about creating a date formatter that exhibits the old Pre 10.4 behavior? Well, it’s actually quite simple. When you create your instance of NSDateFormatter, use the
initWithDateFormat:allowNaturalLanguage:initializer, instead of the usualinitmethod.You should note:Once you create the Pre 10.4 date formatter with a format string, you cannot change it. Though NSDateFormatter has a methodsetDateFormat:, it only applies to the new date formatter behavior. You can however, convert the date formatter to the newer behavior by callingsetDateFormatterBehavior:and passing inNSDateFormatterBehavior10_4.Sample code
NSDateFormatter *df = [[[NSDateFormatter alloc] initWithDateFormat:@"%1m/%1d/%y" allowNaturalLanguage:YES] autorelease]; // The following code will print something like 2/11/2007. Also notice how // putting the '1' before the 'm' and 'd' in our format string took the padding // zeros away. NSLog(@"%@", [df stringForObjectValue:[NSDate date]]); - 10.4+ Mode
NSDateFormatterBehavior10_4
Arguably the behavior you will want to use in your code is the 10.4+ Mode. This mode conforms to the Unicode TR35-4 standard, so it has a ton of features that the Pre 10.4 behavior just doesn’t have. For one, it uses different identifiers, the Unicode site at this link has all of the identifiers and how you can use them to construct date format strings. The documentation is mostly accurate except that though ICU claims three lower case e’s (”eee”) should evaluate to something like “Tues” or “Wed”, that isn’t reflected by the NSDateFormatter.Changing the date format string
One of the really nice things about the new date formatter behavior is that you can set the date format string to something else after you create it. If you callsetDateFormat:with some string, the changes are made immediately. This is nice if your date format string is dynamic.Date and Time Styles
Another feature of the 10.4+ behavior is that you can tell it to ignore date format strings all together, and instead use the time styles defined by the user in System Preferences. These are set by each user on their own system in the International pane of System Preferences:

You can set a date or time style such as short, medium, long, full, or none at all. The methods for doing this are
setDateStyle:andsetTimeStyle:. The tricky part about using these styles programmatically comes in the order that you call those methods. For example, if you callsetDateStyle:orsetTimeStyle:, it voids the effect of the date format string. However, if after callingsetDateStyle:orsetTimeStyle:, you set the date format string by callingsetDateFormat:, it then voids the time and date styles you just applied. The general rule of thumb is that whatever you call last is what the date formatter uses. If you set the date or time style last, it uses that; if you set the date format string last, then it uses that. The code below illustrates these little quirks.// Create our date formatter and a sample date to work with NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease]; NSDate *date = [NSDate date]; // This will produce a date such as "07/11/2007 22:15". [formatter setDateStyle:NSDateFormatterShortStyle]; [formatter setTimeStyle:NSDateFormatterShortStyle]; NSLog(@"%@", [formatter stringFromDate:date]); // This will produce a date such as "07/11/2007", notice, without the time. [formatter setTimeStyle:NSDateFormatterNoStyle]; NSLog(@"%@", [formatter stringFromDate:date]); // This will produce a date such as "7 Nov 2007", thus nullifying the date // and time styles we set above. [formatter setDateFormat:@"d MMM yyyy"]; NSLog(@"%@", [formatter stringFromDate:date]); // But now if I reset the date style, we nullify our date format string. // And though I didn't reset the time style, since I reset the date style, // the formatter assumes the use of both. [formatter setDateStyle:NSDateFormatterShortStyle]; NSLog(@"%@", [formatter stringFromDate:date]);
The NSDateFormatter can be a tricky or intimidating class at first, but I think when you really unpack it, it is quite easy. Just remember:
- Two modes: pre 10.4 (
strftime-style,NSDateFormatterBehavior10_0) and 10.4+ (Unicode,NSDateFormatterBehavior10_4). - When using pre 10.4 behavior, use
initWithDateFormat:allowNaturalLanguage:to create it. And remember, once you create it, you can’t change the format string. - When using 10.4+ behavior, uses standard
initto create it. You can use a date format string or the date/time styles. And when setting these, remember that the last method you call is the style it uses (date format string versus date/time styles). - Check out the Apple and Unicode documentation for a complete list of identifiers.
Probably the most common question asked about IB 3 is: how do you subclass something? In IB 2, it was easy, right? You could just go to the class browser, find the class you wanted to subclass (typically NSObject), subclass it with a name, and then instantiate an instance of that subclass. But, what happened to the class browser in IB 3? Interface Builder 3 goes back to the idea of M-V-C: model, view, controller. For those of you familiar with this concept, you can ignore this next sentence: model is where you write code pertaining to data, view is where you write code or make graphics for what users see, and then finally, controller is where you write code relating to the response of user events. Thanks to Cocoa, the interface code is all written for us, and we can benefit from just dragging objects out of a library in IB. Our typical use to subclass an object in IB is to create a controller object or something similar that our interface talks to.
Note: Interface Builder is not the place to write code, that is the role of Xcode. IB has the ability to generate code, but more for a traditional sense. So the idea is to create a class in Xcode, and then bring the knowledge of that class into IB. Thanks to the smarts of IB, you don’t have to do any work for this, since IB is in constant communication with the pieces of your project. If you create a class, IB knows about it the next time you switch to IB from Xcode.OK, enough talk, how do I do this? Here is a step-by-step guide to subclassing something to use in IB (the example below is creating a subclass of NSObject called AppController)
- Create your class in Xcode
In your Xcode project, add a new Objective-C class file by choosing New File… from the File menu. Name the file AppController.m. - Instantiate your class in IB
The instantiation process is two-fold. First, switch over to IB, and from the Library, drag an instance of NSObject (which you can find using the filter text-field) into your Document Window.
And your Document Window should look something like this now:

So now you have just instantiated NSObject, but what about your subclass? To make the instance you just created descend from your subclass, select in the Document Window, and then open the Identity Inspector (you can do this from the Tools menu, or if you have the inspector open, it is the sixth segment with the little “i”). Once the identity inspector is open, at the top of inspector, there is a slice called “Class Identity”, and under that is a text-field labeled “Class”. In that text-field, start typing the name of your subclass. As you are typing, by the time you get to “App”, it should have auto-completed the text-field with the name of your subclass. Again, please understand here, you no longer have to tell IB about classes in your project because IB has automatic integration with your project.

And your document window should now look something like this, with the “Object” replaced by “App Controller”.

At this point you are done, but if you would like to add connectivity to your class, proceed to step 3.
- Add actions and outlets
Now that you have an instance of your subclass to work with in IB, you may very well want to add some connectivity between it and the interface. To do so, you can add outlets(IBOutlet) and actions (IBAction) to your class’s header file. To do so, go back to Xcode, and enter them where desired. If you need more help on this, please see the Cocoa tutorials on connections. Once you have created your outlets and actions in Xcode, switch back to IB, and by the time you do so, IB already has parsed your source code and found all of the outlets and actions, and they are now readily available for you to use.
So to quickly recap:
- Create your class in Xcode
- Switch to IB and add an instance of NSObject from the Library
- Change the instances class name to your subclass using the Identity Inspector
- Add actions and outlets
- Realize that subclassing in IB 3 is a lot easier than in IB 2
Leman (that is, Andrew, my roommate) and I might get an apartment next year down here (or up there depending on where you are in the world) at the U of I for next fall. The one we would get is at 203 N. Gregory, and today, we had a chance to look at it.It’s a great apartment, with all of the accomadations a junior in college could need. It’s well-kept, quiet, close to the Engineering Quad, and it has a kitchen!To find out more about the apartment, check out the company’s website.I have posted some pictures of the apartment in the photos section as well.
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.