Thursday, April 12, 2012

ARC and Leaks: Great But Imperfect - Part 1

This week I spent time debugging yet another memory management bug. The exercise served as a not-so-friendly reminder that correct memory management remains a very difficult task even with technologies such as Automatic Reference Counting (ARC) and the Leaks Instrument.

Hopefully most Objective-C developers understand the main limitation of ARC: ARC can't break retain cycles. It is up to the programmer to prevent or break retain cycles. Luckily the Leaks Instrument is available to help since it can detect retain-cycle-memory-leaks.

With this in mind I generally develop my code in two stages:
  1. Develop the code using ARC and try my best not to introduce retain cycles.
  2. Run the code under the Leaks Instrument to find and break any retain cycles I might have accidentally introduced.

Once you are comfortable with these steps it is easy to gain a false sense of confidence in your memory management handling. We need to remember that Leaks, as good as it is, is not perfect. It does not (and can not) guarantee that it will find all memory leaks.

This week I was reminded of this fact when I noticed what appeared to be a retain-cycle-memory-leak in my code but which was not being reported under Leaks.

Below is a much simplified version of the code. The class is called LeakyViewController, it is a subclass of UIViewController, and it maintains a strong reference to itself which creates a simple retain-cycle.

If you create an instance of this class and immediately throw it away, the instance will never be deallocated and Leaks does not report a problem.

So what is the deal? I'll explore this further in parts 2 and 3 of this blog post series.

@interface LeakyViewController ()
@property (nonatomic, strong) NSObject *selfRef;
@end

@implementation LeakyViewController

@synthesize selfRef;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
NSLog(@"allocating a LeakyViewController");
self.selfRef = self;
}
return self;
}

- (void) dealloc {
NSLog(@"deallocating a LeakyViewController");
}

@end


Sunday, April 1, 2012

Android's NinePatch: Better Stretchable Images than iOS

A friend of mine this week told me about NinePatch, a nice image file format developed as part of Android.

A good overview of NinePatch can be found here. It is basically a PNG with extra information included to identify the "stretchable" areas of the image. Android then handles the resizing (and proper stretching) automatically for you.

This is great because it frees the programmer from having to write any code related to stretchable images.

It also adheres to the DRY principal (Don't Repeat Yourself) because all the image information is stored in one place: the image (where it belongs) instead of in two places (the image and the code). This allows a designer, for example, to update the image without requiring any software changes.

iOS doesn't yet have an equivalent to NinePatch but it turns out that there is an open source library that brings NinePatch support to iOS. Here is the announcement from the developer Tortuga 22.

In addition to code improvements another potential benefit to adopting NinePatch might be to increase the image asset reuse between your Android and iOS apps.

I haven't yet tried this myself but hope to soon.

Tuesday, March 27, 2012

File Sizes vs Disk Space Used

This week a friend of mine complained about an Android app consuming much more memory than what he thought should be necessary. It was eating up over 30MB of his memory card space when most comparable apps use far less.

He sent me a copy of the files so I could take a look.

The first thing I noticed was that running the du command on my version of the files showed that they only consumed about 4MB of space. Far less than the 30MB he reported. So why the dramatic difference?

We tend to forget (or at least I do) that filesystems store files in fixed sized blocks (not in bytes).

If the file system's block size is 2K (2048 bytes) than a 1 byte file will physically consume 2048 bytes of disk space. That is space which is no longer available to other files or applications.

The default block size on HFS (OS X / iOS) is 4K (4096 bytes).
My friend had a FAT32 memory card with a block size of 32K (32768 bytes). (*)

Now the files consisted of roughly 1000 images each of which is approximately 1K of data. The total data size then is about 1MB.

But when storing as separate files in whole blocks you get waste which increases as block size increases:

Number of 1K images * block size = total disk space consumed
1000 * 4KB = 3.8 MB
1000 * 32KB = 31.2 MB

Something to keep in mind when considering tradeoffs of how you store data.

References:
(*) According to Microsoft FAT32 partitions that are 32GB or higher have a default block size of 32K. (Note that a block is called a "cluster" in FAT terminology).


Friday, March 23, 2012

My favorite feature in Xcode 4.3


My favorite feature in Xcode 4.3 (LLVM 3.1) is that you no longer need to use forward declarations for private Objective-C methods.

Before this version if you called a private Objective-C method you would get a compiler error if you didn't do one of the following:

1) Move the implementation of the private method above all the calls to it in the source file.
2) Forward declare the method via a category or extension.

#1 sucks because you end up with less readable code. Low level "helper" methods bubble up to the top while the more general methods get pushed to the bottom. Ideally (I think) your source code should be organized and read like a newspaper. The high level concepts / methods should be at the top and the lower level methods on which they depend should be below it.

#2 sucks because the forward declarations are extra work which feel unnecessary and seems to break the DRY principle.

With Xcode 4.3 the restriction has been lifted.

You no longer need to declare private methods and you can call the methods from any where in the source file.

Over the past few years I've been super impressed with how quickly Apple has evolved Objective-C. It's finally starting to look modern. ;-)

My hope for WWDC this year is death to the .h file.

Happy coding.