My team found that we were using a variety of NSDateFormatter
objects throughout our code base, and started looking into how we could avoid the cost/confusion of allocating/initializing common formatters in a bunch of separate places.
One idea we had was to create a category on the NSDateFormatter
class that would provide a reference to a static instance of a commonly configured formatter. For example, we were using the "short time" date formatter in several places, and were looking to add the following class method:
@implementation NSDateFormatter (NSDateFormatter_PDDateFormatters)
static NSDateFormatter * shortTimeFormatter = nil;
+ (NSDateFormatter *) PDSharedShortTimeFormatter {
@synchronized([NSDateFormatter class]){
if( shortTimeFormatter == nil){
// Create new formatter for SHORT times (e.g. 12:00 pm)
shortTimeFormatter = [[NSDateFormatter alloc] init];
[shortTimeFormatter setDateStyle: NSDateFormatterNoStyle];
[shortTimeFormatter setTimeStyle:NSDateFormatterShortStyle];
}
return shortTimeFormatter;
}
return nil;
}
@end
One of the issues I have with this approach is that we are not currently "protecting" the NSDateFormatter
from being changed. Since the formatter is essentially "shared" throughout our application, this could potentially cause problems if another object was to change the formatter's configuration (e.g. time/date style).
Because we are using this internally, I'm not overly concerned with the risk of our team misusing this functionality (i.e. it's a small team, and clearly commented).
However, I was wondering about best practices here.
Is there a way to return an immutable reference to the date formatter? If I return a copy of a formatter, is that any less expensive than doing the alloc/inits that we're doing now?
Is there some other approach to take here?
We'll be up and running with this, but it's always good to get some feedback in writing "better" code.
Normally, you would just not worry about it. Obj-C will let you fiddle with the juicy insides of almost anything. Even
@private
doesn't protect against-valueForKey:_thatFunPrivateIvar
. And if all else fails, you can just invoke runtime functions.However, the simplest workaround here would be to expose an API that internally uses cached formatters, but that provides no access to the formatters it is using. Your code would then use
+[Formatter shortTimeStringFromDate:]
to do what your sample code is doing now. The formatter in question could be lazily allocated, and you could use purgeable memory so cached formatters could be cleared in a LRU fashion under memory pressure.