fill the void

Posted
24 November 2009 @ 10pm

Tagged
development

2 Comments

Cocoa Tip: Extend NSNumber

As I mentioned in my last post, categories are such a nice addition to Objective-C. They allow me to wrap up new functionality in a standard class without subclassing it myself. Below, I extend NSNumber with two handy methods for printing out numbers and bytes in a human-readable version, turning 1,000,000 into 1 M and 976.6 KB, respectively.

@implementation NSNumber (Utilities)

- (NSString *)humanReadableBase10 {
	if (self == nil) return nil;

	NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
	[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
	[formatter setMaximumFractionDigits:1];

	NSString *formattedString = nil;
	uint64_t size = [self unsignedLongLongValue];
	if (size < 1000) {
		NSString *formattedNumber = [formatter stringFromNumber:[NSNumber numberWithFloat:size]];
		formattedString = [NSString stringWithFormat:@"%@ B", formattedNumber];
	}
	else if (size < 1000 * 1000) {
		NSString *formattedNumber = [formatter stringFromNumber:[NSNumber numberWithFloat:size / 1000.0]];
		formattedString = [NSString stringWithFormat:@"%@ KB", formattedNumber];
	}
	else if (size < 1000 * 1000 * 1000) {
		NSString *formattedNumber = [formatter stringFromNumber:[NSNumber numberWithFloat:size / 1000.0 / 1000.0]];
		formattedString = [NSString stringWithFormat:@"%@ MB", formattedNumber];
	}
	else {
		NSString *formattedNumber = [formatter stringFromNumber:[NSNumber numberWithFloat:size / 1000.0 / 1000.0 / 1000.0]];
		formattedString = [NSString stringWithFormat:@"%@ GB", formattedNumber];
	}
	[formatter release];

	return formattedString;
}

- (NSString *)humanReadableBase2 {
	if (self == nil)
		return nil;

	NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
	[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
	[formatter setMaximumFractionDigits:1];

	NSString *formattedString = nil;
	uint64_t size = [self unsignedLongLongValue];
	if (size < 1024) {
		NSString *formattedNumber = [formatter stringFromNumber:[NSNumber numberWithFloat:size]];
		formattedString = [NSString stringWithFormat:@"%@ B", formattedNumber];
	}
	else if (size < 1024 * 1024) {
		NSString *formattedNumber = [formatter stringFromNumber:[NSNumber numberWithFloat:size / 1024.0]];
		formattedString = [NSString stringWithFormat:@"%@ KB", formattedNumber];
	}
	else if (size < 1024 * 1024 * 1024) {
		NSString *formattedNumber = [formatter stringFromNumber:[NSNumber numberWithFloat:size / 1024.0 / 1024.0]];
		formattedString = [NSString stringWithFormat:@"%@ MB", formattedNumber];
	}
	else {
		NSString *formattedNumber = [formatter stringFromNumber:[NSNumber numberWithFloat:size / 1024.0 / 1024.0 / 1024.0]];
		formattedString = [NSString stringWithFormat:@"%@ GB", formattedNumber];
	}
	[formatter release];

	return formattedString;
}

@end

UPDATE: I changed the method names to humanReadableBase2 and humanReadableBase10 per Sean’s point.


2 Comments

Posted by
Sean
8 December 2009 @ 11am

You should probably be dividing by 1000 not 1024. :) mega = 1000, mebi = 1024. See http://en.wikipedia.org/wiki/Binary_prefixes#IEC_standard_prefixes As of 10.6 Apple reports memory sizes consistently in base 2 and other sizes (file, disk, etc.) in base 10. Though they annoyingly use the base 10 prefix in all instances.


Posted by
bdunagan
13 December 2009 @ 9pm

Excellent point! I’d forgotten about that change. I updated the code to reflect that. Thanks!


Leave a Comment